gdkproperty-win32.c \
gdkscreen-win32.c \
gdkselection-win32.c \
+ gdkselection-win32.h \
gdktestutils-win32.c \
gdkwin32cursor.h \
gdkwin32display.h \
gdkwin32displaymanager.h \
gdkwin32dnd.h \
+ gdkwin32dnd-private.h \
gdkwin32glcontext.h \
gdkwin32.h \
gdkwin32id.c \
case WM_DRAWCLIPBOARD:
{
HWND hwnd_owner;
+ HWND stored_hwnd_owner;
HWND hwnd_opener;
GdkEvent *event;
GdkWindow *owner;
+ GdkWindow *stored_owner;
+ GdkWin32Selection *win32_sel = _gdk_win32_selection_get ();
hwnd_owner = GetClipboardOwner ();
#ifdef G_ENABLE_DEBUG
if (_gdk_debug_flags & GDK_DEBUG_DND)
{
- if (OpenClipboard (hwnd))
+ if (win32_sel->clipboard_opened_for != INVALID_HANDLE_VALUE ||
+ OpenClipboard (hwnd))
{
UINT nFormat = 0;
while ((nFormat = EnumClipboardFormats (nFormat)) != 0)
g_print ("%s ", _gdk_win32_cf_to_string (nFormat));
- CloseClipboard ();
+ if (win32_sel->clipboard_opened_for == INVALID_HANDLE_VALUE)
+ CloseClipboard ();
}
else
{
if (owner == NULL)
owner = gdk_win32_window_foreign_new_for_display (_gdk_display, hwnd_owner);
+ stored_owner = _gdk_win32_display_get_selection_owner (gdk_display_get_default (),
+ GDK_SELECTION_CLIPBOARD);
+
+ if (stored_owner)
+ stored_hwnd_owner = GDK_WINDOW_HWND (stored_owner);
+ else
+ stored_hwnd_owner = NULL;
+
+ if (stored_hwnd_owner != hwnd_owner)
+ {
+ if (win32_sel->clipboard_opened_for != INVALID_HANDLE_VALUE)
+ {
+ CloseClipboard ();
+ GDK_NOTE (DND, g_print ("Closed clipboard @ %s:%d\n", __FILE__, __LINE__));
+ }
+
+ win32_sel->clipboard_opened_for = INVALID_HANDLE_VALUE;
+
+ _gdk_win32_clear_clipboard_queue ();
+ }
+
event = gdk_event_new (GDK_OWNER_CHANGE);
event->owner_change.window = gdk_get_default_root_window ();
event->owner_change.owner = owner;
static gboolean
gdk_win32_display_supports_clipboard_persistence (GdkDisplay *display)
{
- return FALSE;
+ return TRUE;
}
static void
gdk_win32_display_store_clipboard (GdkDisplay *display,
- GdkWindow *clipboard_window,
- guint32 time_,
- const GdkAtom *targets,
- gint n_targets)
+ GdkWindow *clipboard_window,
+ guint32 time_,
+ const GdkAtom *targets,
+ gint n_targets)
{
+ GdkEvent tmp_event;
+ SendMessage (GDK_WINDOW_HWND (clipboard_window), WM_RENDERALLFORMATS, 0, 0);
+
+ memset (&tmp_event, 0, sizeof (tmp_event));
+ tmp_event.selection.type = GDK_SELECTION_NOTIFY;
+ tmp_event.selection.window = clipboard_window;
+ tmp_event.selection.send_event = FALSE;
+ tmp_event.selection.selection = _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_CLIPBOARD_MANAGER);
+ tmp_event.selection.target = 0;
+ tmp_event.selection.property = GDK_NONE;
+ tmp_event.selection.requestor = 0;
+ tmp_event.selection.time = GDK_CURRENT_TIME;
+
+ gdk_event_put (&tmp_event);
}
static gboolean
#include <fcntl.h>
/*
- * Comment from the old OLE2 DND code that is being merged in. Note
- * that this comment might not fully reflect reality as the code
- * obviously will have to be modified in this merge. Especially the
- * talk about supporting other than UTF-8 text is bogus, that will not
- * happen.
- *
* Support for OLE-2 drag and drop added at Archaeopteryx Software, 2001
* For more information, contact Stephan R.A. Deibel (sdeibel@archaeopteryx.com)
*
- * Notes on implementation:
+ * Notes on the implementation:
+ *
+ * Source drag context, IDragSource and IDataObject for it are created
+ * (almost) simultaneously, whereas target drag context and IDropTarget
+ * are separated in time - IDropTarget is created when a window is made
+ * to accept drops, while target drag context is created when a dragging
+ * cursor enters the window and is destroyed when that cursor leaves
+ * the window.
+ *
+ * There's a mismatch between data types supported by W32 (W32 formats)
+ * and by GTK+ (GDK targets).
+ * To account for it the data is transmuted back and forth. There are two
+ * main points of transmutation:
+ * * GDK convert selection: transmute W32 data to GTK+ data
+ * * GDK window property change: transmute GTK+ data to W32 data
+ *
+ * There are also two points where data formats are considered:
+ * * When source drag context is created, it gets a list of GTK+ targets
+ * that it supports, these are matched to the W32 formats they
+ * correspond to (possibly with transmutation). New W32 formats for
+ * GTK+-specific formats are also created here (see below).
+ * * When target drag context is created, it queries the IDataObject
+ * for the list of W32 formats it supports and matches these to
+ * corresponding GTK+ formats that it will be able to provide
+ * (possibly with transmutation) later. Missing GDK targets for
+ * W32-specific formats are also created here (see below).
*
- * This is a first pass at OLE2 support. It only supports text and unicode text
- * data types, and file list dnd (which is handled seperately as it predates OLE2
- * both in this implementation and on Windows in general).
+ * W32 formats and GTK+ targets are both integers (CLIPFORMAT and GdkAtom
+ * respectively), but cannot be used interchangeably.
*
- * As such, the data type conversion from gdk selection targets to OLE2 CF_* data
- * type specifiers is partially hardwired. Fixing this is complicated by (a) the
- * fact that the widget’s declared selection types aren’t accessible in calls here
- * that need to declare the corresponding OLE2 data types, and (b) there isn’t a
- * 1-1 correspondence between gdk target types and OLE2 types. The former needs
- * some redesign in gtk dnd (something a gdk/gtk expert should do; I have tried
- * and failed!). As an example of the latter: gdk STRING, TEXT, COMPOUND_TEXT map
- * to CF_TEXT, CF_OEMTEXT, and CF_UNICODETEXT but as a group and with conversions
- * necessary for various combinations. Currently, the code here (and in
- * gdkdnd-win32.c) can handle gdk STRING and TEXT but not COMPOUND_TEXT, and OLE2
- * CF_TEXT and CF_UNICODETEXT but not CF_OEMTEXT. The necessary conversions are
- * supplied by the implementation here.
+ * To accommodate advanced GTK+ applications the code allows them to
+ * register drop targets that accept W32 data formats, and to register
+ * drag sources that provide W32 data formats. To do that they must
+ * register either with the string name of the format in question
+ * (for example, "Shell IDList Array") or, for unnamed pre-defined
+ * formats, register with the stringified constant name of the format
+ * in question (for example, "CF_UNICODETEXT").
+ * If such target format is accepted/provided, GDK will not try to
+ * transmute it to/from something else. Otherwise GDK will do the following
+ * transmutation:
+ * * If GTK+ application provides image/png, image/gif or image/jpeg,
+ * GDK will claim to also provide "PNG", "GIF" or "JFIF" respectively,
+ * and will pass these along verbatim.
+ * * If GTK+ application provides any GdkPixbuf-compatible target,
+ * GDK will also offer "PNG" and CF_DIB W32 formats.
+ * * If GTK+ application provides UTF8_STRING, GDK will also offer
+ * CF_UNICODETEXT (UTF-16-encoded) and CF_TEXT (encoded with thread-
+ * and locale-depenant codepage), and will do the conversion when such
+ * data is requested.
+ * * If GTK+ application accepts image/png, image/gif or image/jpeg,
+ * GDK will claim to also accept "PNG", "GIF" or "JFIF" respectively,
+ * and will pass these along verbatim.
+ * * If GTK+ application accepts image/bmp, GDK will
+ * claim to accept CF_DIB W32 format, and will convert
+ * it, changing the header, when such data is provided.
+ * * If GTK+ application accepts UTF8_STRING, GDK will
+ * claim to accept CF_UNICODETEXT and CF_TEXT, and will do
+ * the conversion when such data is provided.
+ * * If GTK+ application accepts text/uri-list, GDK will
+ * claim to accept "Shell IDList Array", and will do the
+ * conversion when such data is provided.
*
- * Note that in combination with another hack originated by Archaeopteryx
- * Software, the text conversions here may go to utf-8 unicode as the standard
- * within-gtk target or to single-byte ascii when the USE_ACP_TEXT compilation
- * flag is TRUE. This mode was added to support applications that aren’t using
- * utf-8 across the gtk/gdk API but instead use single-byte ascii according to
- * the current Windows code page. See gdkim-win32.c for more info on that.
+ * Currently the conversion from text/uri-list to Shell IDList Array is not
+ * implemented, so it's not possible to drag & drop files from GTK+
+ * applications to non-GTK+ applications the same way one can drag files
+ * from Windows Explorer.
*
+ * To accommodate GTK+ application compaibility the code allows
+ * GTK+ applications to register drop targets that accept GTK+-specific
+ * data formats, and to register drag sources that provide GTK+-specific
+ * data formats. This is done by simply registering target atom names
+ * as clipboard formats. This way two GTK+ applications can exchange
+ * data in their native formats (both well-known ones, such as UTF8_STRING,
+ * and special, known only to specific applications). This will work just
+ * fine as long as both applications agree on what kind of data is stored
+ * under such format exactly.
+ *
+ * Note that clipboard format space is limited, there can only be 16384
+ * of them for a particular user session. Therefore it is highly inadvisable
+ * to create and register such formats out of the whole cloth, dynamically.
+ * If more flexibility is needed, register one format that has some
+ * internal indicators of the kind of data it contains, then write the application
+ * in such a way that it requests the data and inspects its header before deciding
+ * whether to accept it or not. For details see GTK+ drag & drop documentation
+ * on the "drag-motion" and "drag-data-received" signals.
*/
/* The mingw.org compiler does not export GUIDS in it's import library. To work
#define INITGUID
#endif
+/* For C-style COM wrapper macros */
+#define COBJMACROS
+
#include "gdkdnd.h"
#include "gdkproperty.h"
#include "gdkinternals.h"
#include "gdkprivate-win32.h"
#include "gdkwin32.h"
#include "gdkwin32dnd.h"
+#include "gdkdisplayprivate.h"
#include "gdk/gdkdndprivate.h"
+#include "gdkwin32dnd-private.h"
+#include "gdkdisplay-win32.h"
+#include "gdkselection-win32.h"
#include <ole2.h>
#include <shlobj.h>
#include <shlguid.h>
+#include <objidl.h>
#include <gdk/gdk.h>
#include <glib/gstdio.h>
+/* from gdkselection-win32.c */
+extern GdkAtom *known_pixbuf_formats;
+extern int n_known_pixbuf_formats;
+
+
typedef enum {
GDK_DRAG_STATUS_DRAG,
GDK_DRAG_STATUS_MOTION_WAIT,
GDK_DRAG_STATUS_DROP
} GdkDragStatus;
-struct _GdkWin32DragContext
-{
- GdkDragContext context;
-
- guint drag_status : 4; /* Current status of drag */
- guint drop_failed : 1; /* Whether the drop was unsuccessful */
-
- POINT ole2_dnd_last_pt; /* Coordinates from last event */
- DWORD ole2_dnd_last_key_state; /* Key state from last event */
- gboolean ole2_dnd_being_finalized;
- gint ole2_dnd_ref_count;
- IUnknown *ole2_dnd_iface;
-};
-
-struct _GdkWin32DragContextClass
-{
- GdkDragContextClass parent_class;
-};
-
static GList *contexts;
static GdkDragContext *current_dest_drag = NULL;
static gboolean use_ole2_dnd = FALSE;
G_DEFINE_TYPE (GdkWin32DragContext, gdk_win32_drag_context, GDK_TYPE_DRAG_CONTEXT)
+static void
+move_drag_window (GdkDragContext *context,
+ guint x_root,
+ guint y_root)
+{
+ GdkWin32DragContext *context_win32 = GDK_WIN32_DRAG_CONTEXT (context);
+
+ gdk_window_move (context_win32->drag_window,
+ x_root - context_win32->hot_x,
+ y_root - context_win32->hot_y);
+ gdk_window_raise (context_win32->drag_window);
+}
+
static void
gdk_win32_drag_context_init (GdkWin32DragContext *context)
{
}
else
{
- context->ole2_dnd_being_finalized = FALSE;
- context->ole2_dnd_ref_count = 1;
- context->ole2_dnd_iface = NULL;
}
GDK_NOTE (DND, g_print ("gdk_drag_context_init %p\n", context));
{
GdkDragContext *context;
GdkWin32DragContext *context_win32;
+ GdkWindow *drag_window;
GDK_NOTE (DND, g_print ("gdk_drag_context_finalize %p\n", object));
g_return_if_fail (GDK_IS_WIN32_DRAG_CONTEXT (object));
context = GDK_DRAG_CONTEXT (object);
- context_win32 = GDK_WIN32_DRAG_CONTEXT (object);
+ context_win32 = GDK_WIN32_DRAG_CONTEXT (context);
if (!use_ole2_dnd)
{
if (context == current_dest_drag)
current_dest_drag = NULL;
}
- else
- {
- if (context_win32->ole2_dnd_iface)
- {
- context_win32->ole2_dnd_being_finalized = TRUE;
- context_win32->ole2_dnd_iface->lpVtbl->Release (context_win32->ole2_dnd_iface);
- context_win32->ole2_dnd_iface = NULL;
- }
- }
+
+ drag_window = context_win32->drag_window;
+
+ g_array_unref (context_win32->droptarget_format_target_map);
G_OBJECT_CLASS (gdk_win32_drag_context_parent_class)->finalize (object);
+
+ if (drag_window)
+ gdk_window_destroy (drag_window);
}
/* Drag Contexts */
gdk_drag_context_new (GdkDisplay *display)
{
GdkWin32DragContext *context_win32;
+ GdkWin32Display *win32_display = GDK_WIN32_DISPLAY (display);
GdkDragContext *context;
context_win32 = g_object_new (GDK_TYPE_WIN32_DRAG_CONTEXT, NULL);
gdk_drag_context_set_device (context, gdk_seat_get_pointer (gdk_display_get_default_seat (display)));
+ if (win32_display->has_fixed_scale)
+ context_win32->scale = win32_display->window_scale;
+ else
+ context_win32->scale = _gdk_win32_display_get_monitor_scale_factor (win32_display, NULL, NULL, NULL);
+
+ context_win32->droptarget_format_target_map = g_array_new (FALSE, FALSE, sizeof (GdkSelTargetFormat));
+
return context;
}
((guchar *) guid)[14], \
((guchar *) guid)[15]);
-
-static FORMATETC *formats;
-static int nformats;
-
typedef struct {
IDropTarget idt;
GdkDragContext *context;
+
+ gint ref_count;
+ GdkWindow *dest_window;
+
} target_drag_context;
typedef struct {
IDropSource ids;
GdkDragContext *context;
+ gint ref_count;
} source_drag_context;
typedef struct {
IDataObject ido;
int ref_count;
GdkDragContext *context;
+ GArray *formats;
} data_object;
typedef struct {
IEnumFORMATETC ief;
int ref_count;
int ix;
+ data_object *dataobj;
} enum_formats;
static source_drag_context *pending_src_context = NULL;
-static IDataObject *dnd_data = NULL;
+static source_drag_context *current_src_context = NULL;
+static data_object *current_src_object = NULL;
-static enum_formats *enum_formats_new (void);
+static enum_formats *enum_formats_new (data_object *dataobj);
/* map windows -> target drag contexts. The table
* owns a ref to both objects.
idroptarget_addref (LPDROPTARGET This)
{
target_drag_context *ctx = (target_drag_context *) This;
- GdkWin32DragContext *context_win32 = GDK_WIN32_DRAG_CONTEXT (ctx->context);
- int ref_count = ++context_win32->ole2_dnd_ref_count;
+ int ref_count = ++ctx->ref_count;
GDK_NOTE (DND, g_print ("idroptarget_addref %p %d\n", This, ref_count));
- g_object_ref (G_OBJECT (ctx->context));
return ref_count;
}
idroptarget_release (LPDROPTARGET This)
{
target_drag_context *ctx = (target_drag_context *) This;
- GdkWin32DragContext *context_win32 = GDK_WIN32_DRAG_CONTEXT (ctx->context);
- int ref_count = --context_win32->ole2_dnd_ref_count;
+ int ref_count = --ctx->ref_count;
GDK_NOTE (DND, g_print ("idroptarget_release %p %d\n", This, ref_count));
- if (!context_win32->ole2_dnd_being_finalized)
- g_object_unref (G_OBJECT (ctx->context));
-
if (ref_count == 0)
- g_free (This);
-
- return ref_count;
-}
-
-#if 0
-
-static GdkAtom
-cf_to_atom (CLIPFORMAT cf)
-{
- switch (cf)
{
- case CF_UNICODETEXT:
- return _utf8_string;
- case CF_HDROP:
- return _text_uri_list;
- case CF_DIB:
- return _image_bmp;
+ g_object_unref (ctx->context);
+ g_clear_object (&ctx->dest_window);
+ g_free (This);
}
- if (cf == _cf_url)
- return _text_uri_list;
-
- if (cf == _cf_html_format || cf == _cf_text_html)
- return _text_html;
-
- return GDK_NONE;
+ return ref_count;
}
-#endif
-
static GdkDragAction
get_suggested_action (DWORD grfKeyState)
{
+ GdkWin32Selection *sel_win32 = _gdk_win32_selection_get ();
/* This is the yucky Windows standard: Force link action if both
* Control and Alt are down, copy if Control is down alone, move if
* Alt is down alone, or use default of move within the app or copy
return GDK_ACTION_COPY;
else if (grfKeyState & MK_ALT)
return GDK_ACTION_MOVE;
-#if 0 /* Default is always copy for now */
- else if (_dnd_source_state == GDK_WIN32_DND_DRAGGING)
+ else if (sel_win32->dnd_source_state == GDK_WIN32_DND_DRAGGING)
return GDK_ACTION_MOVE;
-#endif
else
return GDK_ACTION_COPY;
/* Any way to determine when to add in DROPEFFECT_SCROLL? */
}
}
+static GdkDragAction
+action_for_drop_effect (DWORD effect)
+{
+ switch (effect)
+ {
+ case DROPEFFECT_MOVE:
+ return GDK_ACTION_MOVE;
+ case DROPEFFECT_LINK:
+ return GDK_ACTION_LINK;
+ case DROPEFFECT_COPY:
+ return GDK_ACTION_COPY;
+ default:
+ return 0;
+ }
+}
+
static void
dnd_event_put (GdkEventType type,
GdkDragContext *context,
- const POINTL pt,
+ gint pt_x,
+ gint pt_y,
gboolean to_dest_window)
{
GdkEvent *e;
e = gdk_event_new (type);
if (to_dest_window)
- e->dnd.window = context->dest_window;
+ g_set_object (&e->dnd.window, context->dest_window);
else
- e->dnd.window = context->source_window;
+ g_set_object (&e->dnd.window, context->source_window);
e->dnd.send_event = FALSE;
- e->dnd.context = g_object_ref (context);
+ g_set_object (&e->dnd.context, context);
e->dnd.time = GDK_CURRENT_TIME;
- e->dnd.x_root = pt.x + _gdk_offset_x;
- e->dnd.y_root = pt.y + _gdk_offset_y;
-
- if (e->dnd.window != NULL)
- g_object_ref (e->dnd.window);
+ e->dnd.x_root = pt_x;
+ e->dnd.y_root = pt_y;
gdk_event_set_device (e, gdk_drag_context_get_device (context));
gdk_event_set_seat (e, gdk_device_get_seat (gdk_drag_context_get_device (context)));
gdk_event_free (e);
}
+static GList *
+query_targets (LPDATAOBJECT pDataObj,
+ GArray *format_target_map)
+{
+ IEnumFORMATETC *pfmt = NULL;
+ FORMATETC fmt;
+ GList *result = NULL;
+ HRESULT hr;
+
+ if ((LPDATAOBJECT) current_src_object == pDataObj)
+ return g_list_copy (current_src_object->context->targets);
+
+ hr = IDataObject_EnumFormatEtc (pDataObj, DATADIR_GET, &pfmt);
+
+ if (SUCCEEDED (hr))
+ hr = IEnumFORMATETC_Next (pfmt, 1, &fmt, NULL);
+
+ while (SUCCEEDED (hr) && hr != S_FALSE)
+ {
+ gboolean is_predef;
+ gchar *registered_name = _gdk_win32_get_clipboard_format_name (fmt.cfFormat, &is_predef);
+
+ if (registered_name && is_predef)
+ GDK_NOTE (DND, g_print ("supported built-in source format 0x%x %s\n", fmt.cfFormat, registered_name));
+ else if (registered_name)
+ GDK_NOTE (DND, g_print ("supported source format 0x%x %s\n", fmt.cfFormat, registered_name));
+ else
+ GDK_NOTE (DND, g_print ("supported unnamed? source format 0x%x\n", fmt.cfFormat));
+
+ g_free (registered_name);
+
+ _gdk_win32_add_format_to_targets (fmt.cfFormat, format_target_map, &result);
+ hr = IEnumFORMATETC_Next (pfmt, 1, &fmt, NULL);
+ }
+
+ if (pfmt)
+ IEnumFORMATETC_Release (pfmt);
+
+ return g_list_reverse (result);
+}
+
+static void
+set_data_object (LPDATAOBJECT *location, LPDATAOBJECT data_object)
+{
+ if (*location != NULL)
+ IDataObject_Release (*location);
+
+ *location = data_object;
+
+ if (*location != NULL)
+ IDataObject_AddRef (*location);
+}
+
static HRESULT STDMETHODCALLTYPE
idroptarget_dragenter (LPDROPTARGET This,
LPDATAOBJECT pDataObj,
LPDWORD pdwEffect)
{
target_drag_context *ctx = (target_drag_context *) This;
+ GdkWin32Selection *sel_win32 = _gdk_win32_selection_get ();
+ GdkDragContext *context;
+ GdkWin32DragContext *context_win32;
+ gint pt_x;
+ gint pt_y;
+
+ GDK_NOTE (DND, g_print ("idroptarget_dragenter %p @ %ld : %ld for dest window 0x%p S_OK\n", This, pt.x, pt.y, ctx->dest_window));
+
+ g_clear_object (&ctx->context);
+
+ context = gdk_drag_context_new (gdk_window_get_display (ctx->dest_window));
+ context_win32 = GDK_WIN32_DRAG_CONTEXT (context);
+ ctx->context = context;
+ g_set_object (&context->dest_window, ctx->dest_window);
+
+ context->protocol = GDK_DRAG_PROTO_OLE2;
+ context->is_source = FALSE;
+ context->source_window = NULL;
+
+ /* OLE2 DnD does not allow us to get the source window,
+ * but we *can* find it if it's ours. This is needed to
+ * support DnD within the same widget, for example.
+ */
+ if (current_src_context && current_src_context->context)
+ g_set_object (&context->source_window, current_src_context->context->source_window);
+
+ g_set_object (&sel_win32->target_drag_context, context);
+ context->actions = GDK_ACTION_DEFAULT | GDK_ACTION_COPY | GDK_ACTION_MOVE;
+ context->suggested_action = GDK_ACTION_MOVE;
+ context->action = GDK_ACTION_MOVE;
- GDK_NOTE (DND, g_print ("idroptarget_dragenter %p S_OK\n", This));
+ g_array_set_size (context_win32->droptarget_format_target_map, 0);
+ context->targets = query_targets (pDataObj, context_win32->droptarget_format_target_map);
ctx->context->suggested_action = get_suggested_action (grfKeyState);
- dnd_event_put (GDK_DRAG_ENTER, ctx->context, pt, TRUE);
+ set_data_object (&sel_win32->dnd_data_object_target, pDataObj);
+ pt_x = pt.x / context_win32->scale + _gdk_offset_x;
+ pt_y = pt.y / context_win32->scale + _gdk_offset_y;
+ dnd_event_put (GDK_DRAG_ENTER, ctx->context, pt_x, pt_y, TRUE);
+ dnd_event_put (GDK_DRAG_MOTION, ctx->context, pt_x, pt_y, TRUE);
+ context_win32->last_key_state = grfKeyState;
+ context_win32->last_x = pt_x;
+ context_win32->last_y = pt_y;
process_pending_events (gdk_device_get_display (gdk_drag_context_get_device (ctx->context)));
*pdwEffect = drop_effect_for_action (ctx->context->action);
- /* Assume that target can accept the data: In fact it may fail but
- * we are not really set up to query the target!
- */
+ GDK_NOTE (DND, g_print ("idroptarget_dragenter returns with action %d and drop effect %lu\n", ctx->context->action, *pdwEffect));
+
return S_OK;
}
LPDWORD pdwEffect)
{
target_drag_context *ctx = (target_drag_context *) This;
-
- GDK_NOTE (DND, g_print ("idroptarget_dragover %p S_OK\n", This));
+ GdkWin32DragContext *context_win32 = GDK_WIN32_DRAG_CONTEXT (ctx->context);
+ gint pt_x = pt.x / context_win32->scale + _gdk_offset_x;
+ gint pt_y = pt.y / context_win32->scale + _gdk_offset_y;
ctx->context->suggested_action = get_suggested_action (grfKeyState);
- dnd_event_put (GDK_DRAG_MOTION, ctx->context, pt, TRUE);
+
+ GDK_NOTE (DND, g_print ("idroptarget_dragover %p @ %ld : %ld, suggests %d action S_OK\n", This, pt.x, pt.y, ctx->context->suggested_action));
+
+ if (pt_x != context_win32->last_x ||
+ pt_y != context_win32->last_y ||
+ grfKeyState != context_win32->last_key_state)
+ {
+ dnd_event_put (GDK_DRAG_MOTION, ctx->context, pt_x, pt_y, TRUE);
+ context_win32->last_key_state = grfKeyState;
+ context_win32->last_x = pt_x;
+ context_win32->last_y = pt_y;
+ }
+
process_pending_events (gdk_device_get_display (gdk_drag_context_get_device (ctx->context)));
+
*pdwEffect = drop_effect_for_action (ctx->context->action);
+ GDK_NOTE (DND, g_print ("idroptarget_dragover returns with action %d and effect %lu\n", ctx->context->action, *pdwEffect));
+
return S_OK;
}
idroptarget_dragleave (LPDROPTARGET This)
{
target_drag_context *ctx = (target_drag_context *) This;
- POINTL pt = { 0, 0 };
+ GdkWin32Selection *sel_win32 = _gdk_win32_selection_get ();
GDK_NOTE (DND, g_print ("idroptarget_dragleave %p S_OK\n", This));
- dnd_event_put (GDK_DRAG_LEAVE, ctx->context, pt, TRUE);
+ dnd_event_put (GDK_DRAG_LEAVE, ctx->context, 0, 0, TRUE);
process_pending_events (gdk_device_get_display (gdk_drag_context_get_device (ctx->context)));
+ g_clear_object (&sel_win32->target_drag_context);
+ g_clear_object (&ctx->context);
+ set_data_object (&sel_win32->dnd_data_object_target, NULL);
+
return S_OK;
}
LPDWORD pdwEffect)
{
target_drag_context *ctx = (target_drag_context *) This;
+ GdkWin32DragContext *context_win32 = GDK_WIN32_DRAG_CONTEXT (ctx->context);
+ gint pt_x = pt.x / context_win32->scale + _gdk_offset_x;
+ gint pt_y = pt.y / context_win32->scale + _gdk_offset_y;
+ GdkWin32Selection *sel_win32 = _gdk_win32_selection_get ();
GDK_NOTE (DND, g_print ("idroptarget_drop %p ", This));
if (pDataObj == NULL)
{
GDK_NOTE (DND, g_print ("E_POINTER\n"));
+ g_clear_object (&ctx->context);
return E_POINTER;
}
- dnd_data = pDataObj;
-
ctx->context->suggested_action = get_suggested_action (grfKeyState);
- dnd_event_put (GDK_DROP_START, ctx->context, pt, TRUE);
- process_pending_events (gdk_device_get_display (gdk_drag_context_get_device (ctx->context)));
- dnd_data = NULL;
+ dnd_event_put (GDK_DROP_START, ctx->context, pt_x, pt_y, TRUE);
+ process_pending_events (gdk_device_get_display (gdk_drag_context_get_device (ctx->context)));
/* Notify OLE of copy or move */
- if (_dnd_target_state != GDK_WIN32_DND_DROPPED)
+ if (sel_win32->dnd_target_state != GDK_WIN32_DND_DROPPED)
*pdwEffect = DROPEFFECT_NONE;
else
*pdwEffect = drop_effect_for_action (ctx->context->action);
- GDK_NOTE (DND, g_print ("S_OK\n"));
+ g_clear_object (&sel_win32->target_drag_context);
+ g_clear_object (&ctx->context);
+ set_data_object (&sel_win32->dnd_data_object_target, NULL);
+
+ GDK_NOTE (DND, g_print ("drop S_OK with effect %lx\n", *pdwEffect));
return S_OK;
}
idropsource_addref (LPDROPSOURCE This)
{
source_drag_context *ctx = (source_drag_context *) This;
- GdkWin32DragContext *context_win32 = GDK_WIN32_DRAG_CONTEXT (ctx->context);
- int ref_count = ++context_win32->ole2_dnd_ref_count;
+ int ref_count = ++ctx->ref_count;
GDK_NOTE (DND, g_print ("idropsource_addref %p %d\n", This, ref_count));
- g_object_ref (G_OBJECT (ctx->context));
return ref_count;
}
idropsource_release (LPDROPSOURCE This)
{
source_drag_context *ctx = (source_drag_context *) This;
- GdkWin32DragContext *context_win32 = GDK_WIN32_DRAG_CONTEXT (ctx->context);
- int ref_count = --context_win32->ole2_dnd_ref_count;
+ int ref_count = --ctx->ref_count;
GDK_NOTE (DND, g_print ("idropsource_release %p %d\n", This, ref_count));
- if (!context_win32->ole2_dnd_being_finalized)
- g_object_unref (G_OBJECT (ctx->context));
-
if (ref_count == 0)
+ {
+ g_clear_object (&ctx->context);
+ if (current_src_context == ctx)
+ current_src_context = NULL;
g_free (This);
+ }
return ref_count;
}
{
GdkWin32DragContext *context_win32 = GDK_WIN32_DRAG_CONTEXT (context);
POINT pt;
+ POINT pt_client;
gboolean changed = FALSE;
HWND hwnd = GDK_WINDOW_HWND (context->source_window);
LPARAM lparam;
WPARAM wparam;
+ gint pt_x;
+ gint pt_y;
if (!API_CALL (GetCursorPos, (&pt)))
return FALSE;
- if (!API_CALL (ScreenToClient, (hwnd, &pt)))
+ pt_client = pt;
+
+ if (!API_CALL (ScreenToClient, (hwnd, &pt_client)))
return FALSE;
- if (pt.x != context_win32->ole2_dnd_last_pt.x || pt.y != context_win32->ole2_dnd_last_pt.y ||
- key_state != context_win32->ole2_dnd_last_key_state)
+ pt_x = pt.x / context_win32->scale + _gdk_offset_x;
+ pt_y = pt.y / context_win32->scale + _gdk_offset_y;
+
+ if (pt_x != context_win32->last_x || pt_y != context_win32->last_y ||
+ key_state != context_win32->last_key_state)
{
- lparam = MAKELPARAM (pt.x, pt.y);
+ lparam = MAKELPARAM (pt_client.x, pt_client.y);
wparam = key_state;
- if (pt.x != context_win32->ole2_dnd_last_pt.x || pt.y != context_win32->ole2_dnd_last_pt.y)
+ if (pt_x != context_win32->last_x || pt_y != context_win32->last_y)
{
GDK_NOTE (DND, g_print ("Sending WM_MOUSEMOVE (%ld,%ld)\n", pt.x, pt.y));
SendMessage (hwnd, WM_MOUSEMOVE, wparam, lparam);
}
- if ((key_state & MK_LBUTTON) != (context_win32->ole2_dnd_last_key_state & MK_LBUTTON))
+ if ((key_state & MK_LBUTTON) != (context_win32->last_key_state & MK_LBUTTON))
{
if (key_state & MK_LBUTTON)
SendMessage (hwnd, WM_LBUTTONDOWN, wparam, lparam);
else
SendMessage (hwnd, WM_LBUTTONUP, wparam, lparam);
}
- if ((key_state & MK_MBUTTON) != (context_win32->ole2_dnd_last_key_state & MK_MBUTTON))
+ if ((key_state & MK_MBUTTON) != (context_win32->last_key_state & MK_MBUTTON))
{
if (key_state & MK_MBUTTON)
SendMessage (hwnd, WM_MBUTTONDOWN, wparam, lparam);
else
SendMessage (hwnd, WM_MBUTTONUP, wparam, lparam);
}
- if ((key_state & MK_RBUTTON) != (context_win32->ole2_dnd_last_key_state & MK_RBUTTON))
+ if ((key_state & MK_RBUTTON) != (context_win32->last_key_state & MK_RBUTTON))
{
if (key_state & MK_RBUTTON)
SendMessage (hwnd, WM_RBUTTONDOWN, wparam, lparam);
else
SendMessage (hwnd, WM_RBUTTONUP, wparam, lparam);
}
- if ((key_state & MK_CONTROL) != (context_win32->ole2_dnd_last_key_state & MK_CONTROL))
+ if ((key_state & MK_CONTROL) != (context_win32->last_key_state & MK_CONTROL))
{
if (key_state & MK_CONTROL)
SendMessage (hwnd, WM_KEYDOWN, VK_CONTROL, 0);
else
SendMessage (hwnd, WM_KEYUP, VK_CONTROL, 0);
}
- if ((key_state & MK_SHIFT) != (context_win32->ole2_dnd_last_key_state & MK_SHIFT))
+ if ((key_state & MK_SHIFT) != (context_win32->last_key_state & MK_SHIFT))
{
- if (key_state & MK_CONTROL)
+ if (key_state & MK_SHIFT)
SendMessage (hwnd, WM_KEYDOWN, VK_SHIFT, 0);
else
SendMessage (hwnd, WM_KEYUP, VK_SHIFT, 0);
}
changed = TRUE;
- context_win32->ole2_dnd_last_key_state = key_state;
- context_win32->ole2_dnd_last_pt = pt;
+ context_win32->last_key_state = key_state;
+ context_win32->last_x = pt_x;
+ context_win32->last_y = pt_y;
}
if (esc_pressed)
DWORD grfKeyState)
{
source_drag_context *ctx = (source_drag_context *) This;
+ GdkWin32Selection *sel_win32 = _gdk_win32_selection_get ();
- GDK_NOTE (DND, g_print ("idropsource_querycontinuedrag %p ", This));
+ GDK_NOTE (DND, g_print ("idropsource_querycontinuedrag %p esc=%d keystate=0x%lx ", This, fEscapePressed, grfKeyState));
if (send_change_events (ctx->context, grfKeyState, fEscapePressed))
process_pending_events (gdk_device_get_display (gdk_drag_context_get_device (ctx->context)));
- if (_dnd_source_state == GDK_WIN32_DND_DROPPED)
+ if (sel_win32->dnd_source_state == GDK_WIN32_DND_DROPPED)
{
GDK_NOTE (DND, g_print ("DRAGDROP_S_DROP\n"));
return DRAGDROP_S_DROP;
}
- else if (_dnd_source_state == GDK_WIN32_DND_NONE)
+ else if (sel_win32->dnd_source_state == GDK_WIN32_DND_NONE)
{
GDK_NOTE (DND, g_print ("DRAGDROP_S_CANCEL\n"));
return DRAGDROP_S_CANCEL;
DWORD dwEffect)
{
source_drag_context *ctx = (source_drag_context *) This;
+ GdkWin32DragContext *context_win32 = GDK_WIN32_DRAG_CONTEXT (ctx->context);
GdkDragAction suggested_action;
+ GdkEvent *e;
+ POINT pt;
- GDK_NOTE (DND, g_print ("idropsource_givefeedback %p DRAGDROP_S_USEDEFAULTCURSORS\n", This));
+ GDK_NOTE (DND, g_print ("idropsource_givefeedback %p with drop effect %lu S_OK\n", This, dwEffect));
+
+ if (!API_CALL (GetCursorPos, (&pt)))
+ return S_OK;
+
+ suggested_action = action_for_drop_effect (dwEffect);
- if (dwEffect == DROPEFFECT_MOVE)
- suggested_action = GDK_ACTION_MOVE;
- else
- suggested_action = GDK_ACTION_COPY;
ctx->context->action = suggested_action;
if (dwEffect == DROPEFFECT_NONE)
- {
- if (ctx->context->dest_window != NULL)
- {
- g_object_unref (ctx->context->dest_window);
- ctx->context->dest_window = NULL;
- }
- }
- else
- {
- if (ctx->context->dest_window == NULL)
- ctx->context->dest_window = g_object_ref (gdk_get_default_root_window ());
- }
+ g_clear_object (&ctx->context->dest_window);
+ else if (ctx->context->dest_window == NULL)
+ ctx->context->dest_window = g_object_ref (gdk_get_default_root_window ());
+
+ context_win32->last_x = pt.x / context_win32->scale + _gdk_offset_x;
+ context_win32->last_y = pt.y / context_win32->scale + _gdk_offset_y;
+
+ e = gdk_event_new (GDK_DRAG_STATUS);
+
+ g_set_object (&e->dnd.window, ctx->context->source_window);
+ e->dnd.send_event = FALSE;
+ g_set_object (&e->dnd.context, ctx->context);
+ e->dnd.time = GDK_CURRENT_TIME;
+ e->dnd.x_root = context_win32->last_x;
+ e->dnd.y_root = context_win32->last_y;
+
+ gdk_event_set_device (e, gdk_drag_context_get_device (ctx->context));
+ gdk_event_set_seat (e, gdk_device_get_seat (gdk_drag_context_get_device (ctx->context)));
+
+ GDK_NOTE (EVENTS, _gdk_win32_print_event (e));
+ gdk_event_put (e);
+ gdk_event_free (e);
+ process_pending_events (gdk_device_get_display (gdk_drag_context_get_device (ctx->context)));
- return DRAGDROP_S_USEDEFAULTCURSORS;
+ GDK_NOTE (DND, g_print ("idropsource_givefeedback %p returns\n", This));
+
+ return S_OK;
}
static ULONG STDMETHODCALLTYPE
GDK_NOTE (DND, g_print ("idataobject_release %p %d\n", This, ref_count));
if (ref_count == 0)
- g_free (This);
+ {
+ g_array_free (dobj->formats, TRUE);
+ g_free (This);
+ }
return ref_count;
}
query (LPDATAOBJECT This,
LPFORMATETC pFormatEtc)
{
- int i;
+ data_object *ctx = (data_object *) This;
+ gint i;
if (!pFormatEtc)
return DV_E_FORMATETC;
if ((pFormatEtc->dwAspect & DVASPECT_CONTENT) == 0)
return DV_E_DVASPECT;
- for (i = 0; i < nformats; i++)
- if (pFormatEtc->cfFormat == formats[i].cfFormat)
+ for (i = 0; i < ctx->formats->len; i++)
+ if (pFormatEtc->cfFormat == g_array_index (ctx->formats, GdkSelTargetFormat, i).format)
return S_OK;
return DV_E_FORMATETC;
}
-static FORMATETC *active_pFormatEtc = NULL;
-static STGMEDIUM *active_pMedium = NULL;
-
static HRESULT STDMETHODCALLTYPE
idataobject_getdata (LPDATAOBJECT This,
LPFORMATETC pFormatEtc,
LPSTGMEDIUM pMedium)
{
data_object *ctx = (data_object *) This;
+ GdkWin32Selection *win32_sel = _gdk_win32_selection_get ();
HRESULT hr;
GdkEvent e;
+ gint i;
+ GdkAtom target;
GDK_NOTE (DND, g_print ("idataobject_getdata %p %s ",
This, _gdk_win32_cf_to_string (pFormatEtc->cfFormat)));
/* Check whether we can provide requested format */
hr = query (This, pFormatEtc);
if (hr != S_OK)
- return hr;
+ {
+ GDK_NOTE (DND, g_print ("Unsupported format, returning 0x%lx\n", hr));
+ return hr;
+ }
- /* Append a GDK_SELECTION_GET event and then hope the app sets the
+ /* Append a GDK_SELECTION_REQUEST event and then hope the app sets the
* property associated with the _gdk_ole2_dnd atom
*/
+ win32_sel->property_change_format = pFormatEtc->cfFormat;
+ win32_sel->property_change_data = pMedium;
+
+ for (i = 0, target = NULL; i < ctx->formats->len; i++)
+ {
+ GdkSelTargetFormat *frec = &g_array_index (ctx->formats, GdkSelTargetFormat, i);
+
+ if (frec->format == pFormatEtc->cfFormat)
+ {
+ target = frec->target;
+ win32_sel->property_change_transmute = frec->transmute;
+ }
+ }
+
+ if (target == NULL)
+ {
+ GDK_NOTE (EVENTS, g_print ("(target not found)"));
+ return E_UNEXPECTED;
+ }
- active_pFormatEtc = pFormatEtc;
- active_pMedium = pMedium;
+ GDK_NOTE (DND, {
+ gchar *target_name = gdk_atom_name (target);
+ g_print ("idataobject_getdata will request target 0x%p (%s) ",
+ target, target_name);
+ g_free (target_name);
+ });
+
+ memset (&e, 0, sizeof (GdkEvent));
e.type = GDK_SELECTION_REQUEST;
- e.selection.window = ctx->context->source_window;
+ g_set_object (&e.selection.window, ctx->context->source_window);
e.selection.send_event = FALSE; /* ??? */
- /* FIXME: Should really both selection and property be _gdk_ole2_dnd? */
- e.selection.selection = _gdk_ole2_dnd;
- /* FIXME: Target? */
- e.selection.target = _utf8_string;
- e.selection.property = _gdk_ole2_dnd;
+ /* Both selection and property are OLE2_DND, because change_property()
+ * will only get the property and not the selection. Theoretically we
+ * could use two different atoms (SELECTION_OLE2_DND and PROPERTY_OLE2_DND),
+ * but there's little reason to do so.
+ */
+ e.selection.selection = _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_OLE2_DND);
+ e.selection.target = target;
+ /* Requestor here is fake, just to allow the event to be processed */
+ g_set_object (&e.selection.requestor, ctx->context->source_window);
+ e.selection.property = _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_OLE2_DND);
e.selection.time = GDK_CURRENT_TIME;
- g_object_ref (e.selection.window);
-
GDK_NOTE (EVENTS, _gdk_win32_print_event (&e));
+
gdk_event_put (&e);
+
process_pending_events (gdk_device_get_display (gdk_drag_context_get_device (ctx->context)));
- active_pFormatEtc = NULL;
- active_pMedium = NULL;
+ win32_sel->property_change_format = 0;
+ win32_sel->property_change_data = 0;
if (pMedium->hGlobal == NULL) {
+ GDK_NOTE (DND, g_print (" E_UNEXPECTED\n"));
return E_UNEXPECTED;
}
+ GDK_NOTE (DND, g_print (" S_OK\n"));
return S_OK;
}
hr = query (This, pFormatEtc);
-#define CASE(x) case x: g_print (#x)
+#define CASE(x) case x: g_print (#x "\n"); break
GDK_NOTE (DND, {
- g_print ("idataobject_querygetdata %p %s \n",
+ g_print ("idataobject_querygetdata %p %s ",
This, _gdk_win32_cf_to_string (pFormatEtc->cfFormat));
switch (hr)
{
return E_NOTIMPL;
}
- *ppEnumFormatEtc = &enum_formats_new ()->ief;
+ *ppEnumFormatEtc = &enum_formats_new ((data_object *) This)->ief;
- GDK_NOTE (DND, g_print ("%p S_OK\n", *ppEnumFormatEtc));
+ GDK_NOTE (DND, g_print (" %p S_OK\n", *ppEnumFormatEtc));
return S_OK;
}
GDK_NOTE (DND, g_print ("ienumformatetc_release %p %d\n", This, ref_count));
if (ref_count == 0)
- g_free (This);
+ {
+ idataobject_release ((LPDATAOBJECT) en->dataobj);
+ g_free (This);
+ }
return ref_count;
}
{
enum_formats *en = (enum_formats *) This;
ULONG i, n;
+ ULONG formats_to_get = celt;
GDK_NOTE (DND, g_print ("ienumformatetc_next %p %d %ld ", This, en->ix, celt));
n = 0;
- for (i = 0; i < celt; i++)
+ for (i = 0; i < formats_to_get; i++)
{
- if (en->ix >= nformats)
+ UINT fmt;
+ if (en->ix >= en->dataobj->formats->len)
break;
- elts[i] = formats[en->ix++];
+ fmt = g_array_index (en->dataobj->formats, GdkSelTargetFormat, en->ix++).format;
+ /* skip internals */
+ if (fmt == 0 || fmt > 0xFFFF)
+ {
+ formats_to_get += 1;
+ continue;
+ }
+ elts[n].cfFormat = fmt;
+ elts[n].ptd = NULL;
+ elts[n].dwAspect = DVASPECT_CONTENT;
+ elts[n].lindex = -1;
+ elts[n].tymed = TYMED_HGLOBAL;
+
n++;
}
GDK_NOTE (DND, g_print ("ienumformatetc_clone %p S_OK\n", This));
- new = enum_formats_new ();
+ new = enum_formats_new (en->dataobj);
new->ix = en->ix;
static target_drag_context *
target_context_new (GdkWindow *window)
{
- GdkDragContext *context;
- GdkWin32DragContext *context_win32;
target_drag_context *result;
- context = gdk_drag_context_new (gdk_window_get_display (window));
- context_win32 = GDK_WIN32_DRAG_CONTEXT (context);
-
result = g_new0 (target_drag_context, 1);
- result->context = context;
result->idt.lpVtbl = &idt_vtbl;
+ result->ref_count = 0;
- result->context->protocol = GDK_DRAG_PROTO_OLE2;
- result->context->is_source = FALSE;
-
- result->context->source_window = NULL;
-
- result->context->dest_window = window;
- g_object_ref (window);
+ result->dest_window = g_object_ref (window);
- /* FIXME: context->targets? */
- result->context->actions = GDK_ACTION_DEFAULT | GDK_ACTION_COPY | GDK_ACTION_MOVE;
- result->context->suggested_action = GDK_ACTION_MOVE;
- result->context->action = GDK_ACTION_MOVE;
-
- context_win32->ole2_dnd_iface = (IUnknown *) &result->idt;
idroptarget_addref (&result->idt);
- GDK_NOTE (DND, g_print ("target_context_new: %p\n", result));
+ GDK_NOTE (DND, g_print ("target_context_new: %p (window %p)\n", result, result->dest_window));
return result;
}
GdkDragContext *context;
GdkWin32DragContext *context_win32;
source_drag_context *result;
+ GList *p;
context = gdk_drag_context_new (gdk_window_get_display (window));
context_win32 = GDK_WIN32_DRAG_CONTEXT (context);
result = g_new0 (source_drag_context, 1);
result->context = context;
result->ids.lpVtbl = &ids_vtbl;
+ result->ref_count = 0;
result->context->protocol = GDK_DRAG_PROTO_OLE2;
result->context->is_source = TRUE;
- result->context->source_window = window;
- g_object_ref (window);
+ result->context->source_window = g_object_ref (window);
result->context->dest_window = NULL;
result->context->targets = g_list_copy (targets);
+ context_win32->has_image_format = FALSE;
+ context_win32->has_cf_png = FALSE;
+ context_win32->has_cf_dib = FALSE;
+ context_win32->has_text_uri_list = FALSE;
+ context_win32->has_shell_id_list = FALSE;
+ context_win32->has_unicodetext = FALSE;
+
+ for (p = result->context->targets; p; p = g_list_next (p))
+ {
+ gint j;
+ GdkAtom target = p->data;
+
+ if (target == _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_TEXT_URI_LIST))
+ context_win32->has_text_uri_list = TRUE;
+ else if (target == _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_CFSTR_SHELLIDLIST))
+ context_win32->has_shell_id_list = TRUE;
+ else if (target == _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_PNG))
+ context_win32->has_cf_png = TRUE;
+ else if (target == _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_CF_DIB))
+ context_win32->has_cf_dib = TRUE;
+ else if (target == _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_GIF))
+ context_win32->has_gif = TRUE;
+ else if (target == _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_JFIF))
+ context_win32->has_jfif = TRUE;
+ else if (target == _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_CF_UNICODETEXT))
+ context_win32->has_unicodetext = TRUE;
+
+ for (j = 0; !context_win32->has_image_format && j < _gdk_win32_selection_get ()->n_known_pixbuf_formats; j++)
+ if (target == _gdk_win32_selection_get ()->known_pixbuf_formats[j])
+ context_win32->has_image_format = TRUE;
+ }
- context_win32->ole2_dnd_iface = (IUnknown *) &result->ids;
idropsource_addref (&result->ids);
- GDK_NOTE (DND, g_print ("source_context_new: %p\n", result));
+ GDK_NOTE (DND, g_print ("source_context_new: %p (drag context %p)\n", result, result->context));
+
+ if (current_src_context == NULL)
+ current_src_context = result;
return result;
}
data_object_new (GdkDragContext *context)
{
data_object *result;
+ GList *p;
result = g_new0 (data_object, 1);
result->ido.lpVtbl = &ido_vtbl;
result->ref_count = 1;
result->context = context;
+ result->formats = g_array_new (FALSE, FALSE, sizeof (GdkSelTargetFormat));
+
+ for (p = context->targets; p; p = p->next)
+ {
+ GdkAtom target = p->data;
+ gchar *target_name = gdk_atom_name (target);
+ gint added_count = 0;
+ gint i;
+
+ GDK_NOTE (DND, g_print ("DataObject supports target 0x%p (%s)\n", target, target_name));
+ g_free (target_name);
+
+ added_count = _gdk_win32_add_target_to_selformats (target, result->formats);
+
+ for (i = 0; i < added_count && result->formats->len - 1 - i >= 0; i++)
+ GDK_NOTE (DND, g_print ("DataObject will support format 0x%x\n", g_array_index (result->formats, GdkSelTargetFormat, i).format));
+ }
GDK_NOTE (DND, g_print ("data_object_new: %p\n", result));
}
static enum_formats *
-enum_formats_new (void)
+enum_formats_new (data_object *dataobj)
{
enum_formats *result;
result->ief.lpVtbl = &ief_vtbl;
result->ref_count = 1;
result->ix = 0;
+ result->dataobj = dataobj;
+ idataobject_addref ((LPDATAOBJECT) dataobj);
return result;
}
-void
-_gdk_win32_ole2_dnd_property_change (GdkAtom type,
- gint format,
- const guchar *data,
- gint nelements)
-{
- if (use_ole2_dnd)
- {
- HGLOBAL hdata = NULL;
-
- if (active_pFormatEtc == NULL || active_pMedium == NULL)
- return;
-
- /* Set up the data buffer for wide character text request */
- if (active_pFormatEtc->cfFormat == CF_UNICODETEXT)
- {
- gunichar2 *wdata;
- glong wlen;
-
- wdata = g_utf8_to_utf16 ((const char *) data, -1, NULL, &wlen, NULL);
- hdata = GlobalAlloc(GMEM_MOVEABLE | GMEM_ZEROINIT, (wlen + 1) * 2);
- if (hdata)
- {
- wchar_t *ptr = (wchar_t *) GlobalLock(hdata);
- memcpy (ptr, wdata, (wlen + 1) * 2);
- GlobalUnlock(hdata);
- }
- g_free (wdata);
- }
- else
- g_warning ("Only text handled for now");
-
- /* Pack up data */
- active_pMedium->tymed = TYMED_HGLOBAL;
- active_pMedium->hGlobal = hdata;
- active_pMedium->pUnkForRelease = 0;
- }
-}
-
/* From MS Knowledge Base article Q130698 */
static gboolean
gpointer data)
{
GdkDragContext *context;
+ GdkWin32DragContext *context_win32;
GString *result;
MSG *msg = (MSG *) xev;
HANDLE hdrop;
GDK_NOTE (DND, g_print ("WM_DROPFILES: %p\n", msg->hwnd));
context = gdk_drag_context_new (gdk_window_get_display (event->any.window));
+ context_win32 = GDK_WIN32_DRAG_CONTEXT (context);
context->protocol = GDK_DRAG_PROTO_WIN32_DROPFILES;
context->is_source = FALSE;
context->source_window = gdk_get_default_root_window ();
g_object_ref (context->source_window);
- context->dest_window = event->any.window;
- g_object_ref (context->dest_window);
+ g_set_object (&context->dest_window, event->any.window);
/* WM_DROPFILES drops are always file names */
context->targets =
- g_list_append (NULL, _text_uri_list);
+ g_list_append (NULL, _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_TEXT_URI_LIST));
context->actions = GDK_ACTION_COPY;
context->suggested_action = GDK_ACTION_COPY;
current_dest_drag = context;
DragQueryPoint (hdrop, &pt);
ClientToScreen (msg->hwnd, &pt);
- event->dnd.x_root = pt.x + _gdk_offset_x;
- event->dnd.y_root = pt.y + _gdk_offset_y;
+ event->dnd.x_root = pt.x / context_win32->scale + _gdk_offset_x;
+ event->dnd.y_root = pt.y / context_win32->scale + _gdk_offset_y;
event->dnd.time = _gdk_win32_get_next_tick (msg->time);
nfiles = DragQueryFile (hdrop, 0xFFFFFFFF, NULL, 0);
return GDK_FILTER_CONTINUE;
}
-static void
-add_format (GArray *fmts,
- CLIPFORMAT cf)
-{
- FORMATETC fmt;
-
- fmt.cfFormat = cf;
- fmt.ptd = NULL;
- fmt.dwAspect = DVASPECT_CONTENT;
- fmt.lindex = -1;
- fmt.tymed = TYMED_HGLOBAL;
-
- g_array_append_val (fmts, fmt);
-}
void
if (use_ole2_dnd)
{
HRESULT hr;
- GArray *fmts;
hr = OleInitialize (NULL);
if (! SUCCEEDED (hr))
g_error ("OleInitialize failed");
- fmts = g_array_new (FALSE, FALSE, sizeof (FORMATETC));
-
- /* The most important presumably */
- add_format (fmts, CF_UNICODETEXT);
-
- /* Used for GTK+ internal DND, I think was the intent? Anyway, code below assumes
- * this is at index 1.
- */
- add_format (fmts, CF_GDIOBJFIRST);
-
- add_format (fmts, CF_HDROP);
-
- add_format (fmts, _cf_png);
- add_format (fmts, CF_DIB);
-
- add_format (fmts, _cf_url);
- add_format (fmts, _cf_html_format);
- add_format (fmts, _cf_text_html);
-
- nformats = fmts->len;
- formats = (FORMATETC*) g_array_free (fmts, FALSE);
-
target_ctx_for_window = g_hash_table_new (g_direct_hash, g_direct_equal);
}
}
{
tmp_event = gdk_event_new (GDK_DRAG_LEAVE);
- tmp_event->dnd.window = g_object_ref (context->dest_window);
+ g_set_object (&tmp_event->dnd.window, context->dest_window);
/* Pass ownership of context to the event */
tmp_event->dnd.send_event = FALSE;
- tmp_event->dnd.context = g_object_ref (current_dest_drag);
+ g_set_object (&tmp_event->dnd.context, current_dest_drag);
tmp_event->dnd.time = GDK_CURRENT_TIME; /* FIXME? */
gdk_event_set_device (tmp_event, gdk_drag_context_get_device (context));
gdk_event_set_seat (tmp_event, gdk_device_get_seat (gdk_drag_context_get_device (context)));
new_context->protocol = GDK_DRAG_PROTO_LOCAL;
new_context->is_source = FALSE;
- new_context->source_window = context->source_window;
- g_object_ref (new_context->source_window);
-
- new_context->dest_window = context->dest_window;
- g_object_ref (new_context->dest_window);
+ g_set_object (&new_context->source_window, context->source_window);
+ g_set_object (&new_context->dest_window, context->dest_window);
new_context->targets = g_list_copy (context->targets);
new_context->actions = context->actions;
tmp_event = gdk_event_new (GDK_DRAG_ENTER);
- tmp_event->dnd.window = g_object_ref (context->dest_window);
+ g_set_object (&tmp_event->dnd.window, context->dest_window);
tmp_event->dnd.send_event = FALSE;
- tmp_event->dnd.context = g_object_ref (new_context);
+ g_set_object (&tmp_event->dnd.context, new_context);
tmp_event->dnd.time = GDK_CURRENT_TIME; /* FIXME? */
gdk_event_set_device (tmp_event, gdk_drag_context_get_device (context));
gdk_event_set_seat (tmp_event, gdk_device_get_seat (gdk_drag_context_get_device (context)));
GdkWin32DragContext *current_dest_drag_win32;
tmp_event = gdk_event_new (GDK_DRAG_MOTION);
- tmp_event->dnd.window = g_object_ref (current_dest_drag->dest_window);
+ g_set_object (&tmp_event->dnd.window, current_dest_drag->dest_window);
tmp_event->dnd.send_event = FALSE;
- tmp_event->dnd.context = g_object_ref (current_dest_drag);
+ g_set_object (&tmp_event->dnd.context, current_dest_drag);
tmp_event->dnd.time = time;
gdk_event_set_device (tmp_event, gdk_drag_context_get_device (current_dest_drag));
gdk_event_set_seat (tmp_event, gdk_device_get_seat (gdk_drag_context_get_device (current_dest_drag)));
tmp_event->dnd.y_root = y_root;
current_dest_drag_win32 = GDK_WIN32_DRAG_CONTEXT (current_dest_drag);
- current_dest_drag_win32->ole2_dnd_last_pt.x = x_root - _gdk_offset_x;
- current_dest_drag_win32->ole2_dnd_last_pt.y = y_root - _gdk_offset_y;
+ current_dest_drag_win32->last_x = x_root;
+ current_dest_drag_win32->last_y = y_root;
context_win32->drag_status = GDK_DRAG_STATUS_MOTION_WAIT;
/* Pass ownership of context to the event */
tmp_event = gdk_event_new (GDK_DROP_START);
- tmp_event->dnd.window = g_object_ref (current_dest_drag->dest_window);
+ g_set_object (&tmp_event->dnd.window, current_dest_drag->dest_window);
tmp_event->dnd.send_event = FALSE;
- tmp_event->dnd.context = g_object_ref (current_dest_drag);
+ g_set_object (&tmp_event->dnd.context, current_dest_drag);
tmp_event->dnd.time = GDK_CURRENT_TIME;
gdk_event_set_device (tmp_event, gdk_drag_context_get_device (current_dest_drag));
gdk_event_set_seat (tmp_event, gdk_device_get_seat (gdk_drag_context_get_device (current_dest_drag)));
context_win32 = GDK_WIN32_DRAG_CONTEXT (current_dest_drag);
- tmp_event->dnd.x_root = context_win32->ole2_dnd_last_pt.x + _gdk_offset_x;
- tmp_event->dnd.y_root = context_win32->ole2_dnd_last_pt.y + _gdk_offset_y;
+ tmp_event->dnd.x_root = context_win32->last_x;
+ tmp_event->dnd.y_root = context_win32->last_y;
current_dest_drag = NULL;
local_send_leave (context, time);
}
- g_object_unref (context->dest_window);
- context->dest_window = NULL;
+ g_clear_object (&context->dest_window);
}
}
+static GdkWindow *
+create_drag_window (GdkScreen *screen)
+{
+ GdkWindowAttr attrs = { 0 };
+ guint mask;
+
+ attrs.x = attrs.y = 0;
+ attrs.width = attrs.height = 100;
+ attrs.wclass = GDK_INPUT_OUTPUT;
+ attrs.window_type = GDK_WINDOW_TEMP;
+ attrs.type_hint = GDK_WINDOW_TYPE_HINT_DND;
+ attrs.visual = gdk_screen_get_rgba_visual (screen);
+ if (!attrs.visual)
+ attrs.visual = gdk_screen_get_system_visual (screen);
+
+ mask = GDK_WA_X | GDK_WA_Y | GDK_WA_VISUAL | GDK_WA_TYPE_HINT;
+
+ return gdk_window_new (gdk_screen_get_root_window (screen), &attrs, mask);
+}
+
GdkDragContext *
_gdk_win32_window_drag_begin (GdkWindow *window,
GdkDevice *device,
gint x_root,
gint y_root)
{
+ GdkDragContext *new_context;
+ GdkWin32DragContext *context_win32;
+ BYTE kbd_state[256];
+ GdkWin32Selection *sel_win32 = _gdk_win32_selection_get ();
+
if (!use_ole2_dnd)
{
- GdkDragContext *new_context;
-
g_return_val_if_fail (window != NULL, NULL);
new_context = gdk_drag_context_new (gdk_window_get_display (window));
new_context->is_source = TRUE;
- new_context->source_window = window;
- g_object_ref (window);
+ g_set_object (&new_context->source_window, window);
new_context->targets = g_list_copy (targets);
new_context->actions = 0;
-
- return new_context;
+ context_win32 = GDK_WIN32_DRAG_CONTEXT (new_context);
}
else
{
ctx = source_context_new (window, targets);
- _dnd_source_state = GDK_WIN32_DND_PENDING;
+ sel_win32->dnd_source_state = GDK_WIN32_DND_PENDING;
pending_src_context = ctx;
- g_object_ref (ctx->context);
+ new_context = g_object_ref (ctx->context);
- return ctx->context;
+ context_win32 = GDK_WIN32_DRAG_CONTEXT (new_context);
}
+
+ context_win32->start_x = x_root;
+ context_win32->start_y = y_root;
+ context_win32->last_x = context_win32->start_x;
+ context_win32->last_y = context_win32->start_y;
+
+ context_win32->last_key_state = 0;
+ API_CALL (GetKeyboardState, (kbd_state));
+
+ if (kbd_state[VK_CONTROL] & 0x80)
+ context_win32->last_key_state |= MK_CONTROL;
+ if (kbd_state[VK_SHIFT] & 0x80)
+ context_win32->last_key_state |= MK_SHIFT;
+ if (kbd_state[VK_LBUTTON] & 0x80)
+ context_win32->last_key_state |= MK_LBUTTON;
+ if (kbd_state[VK_MBUTTON] & 0x80)
+ context_win32->last_key_state |= MK_MBUTTON;
+ if (kbd_state[VK_RBUTTON] & 0x80)
+ context_win32->last_key_state |= MK_RBUTTON;
+
+ context_win32->drag_window = create_drag_window (gdk_window_get_screen (window));
+
+ return new_context;
}
void
_gdk_win32_dnd_do_dragdrop (void)
{
- if (use_ole2_dnd)
- {
- GdkDragContext* drag_ctx;
- GdkWin32DragContext *drag_ctx_win32;
- BYTE kbd_state[256];
- data_object *dobj;
- HRESULT hr;
- DWORD dwEffect;
+ GdkDragContext* drag_ctx;
+ data_object *dobj;
+ HRESULT hr;
+ DWORD dwEffect;
-#if 0
- HGLOBAL global;
- STGMEDIUM medium;
-#endif
+ if (!use_ole2_dnd)
+ return;
- if (pending_src_context == NULL)
- return;
+ if (pending_src_context == NULL)
+ return;
- drag_ctx = pending_src_context->context;
- drag_ctx_win32 = GDK_WIN32_DRAG_CONTEXT (drag_ctx);
+ drag_ctx = pending_src_context->context;
- dobj = data_object_new (drag_ctx);
+ dobj = data_object_new (drag_ctx);
+ current_src_object = dobj;
- API_CALL (GetCursorPos, (&drag_ctx_win32->ole2_dnd_last_pt));
- API_CALL (ScreenToClient, (GDK_WINDOW_HWND (drag_ctx->source_window), &drag_ctx_win32->ole2_dnd_last_pt));
- drag_ctx_win32->ole2_dnd_last_key_state = 0;
- API_CALL (GetKeyboardState, (kbd_state));
+ /* Start dragging with mainloop inside the OLE2 API. Exits only when done */
- if (kbd_state[VK_CONTROL])
- drag_ctx_win32->ole2_dnd_last_key_state |= MK_CONTROL;
- if (kbd_state[VK_SHIFT])
- drag_ctx_win32->ole2_dnd_last_key_state |= MK_SHIFT;
- if (kbd_state[VK_LBUTTON])
- drag_ctx_win32->ole2_dnd_last_key_state |= MK_LBUTTON;
- if (kbd_state[VK_MBUTTON])
- drag_ctx_win32->ole2_dnd_last_key_state |= MK_MBUTTON;
- if (kbd_state[VK_RBUTTON])
- drag_ctx_win32->ole2_dnd_last_key_state |= MK_RBUTTON;
+ GDK_NOTE (DND, g_print ("Calling DoDragDrop\n"));
-#if 0
- global = GlobalAlloc (GMEM_FIXED, sizeof (ctx));
-
- memcpy (&global, ctx, sizeof (ctx));
-
- medium.tymed = TYMED_HGLOBAL;
- medium.hGlobal = global;
- medium.pUnkForRelease = NULL;
-
- /* FIXME I wish I remember what I was thinking of here, i.e. what
- * the formats[1] signifies, i.e. the CF_GDIOBJFIRST FORMATETC?
- */
- dobj->ido.lpVtbl->SetData (&dobj->ido, &formats[1], &medium, TRUE);
-#endif
-
- /* Start dragging with mainloop inside the OLE2 API. Exits only when done */
-
- GDK_NOTE (DND, g_print ("Calling DoDragDrop\n"));
-
- _gdk_win32_begin_modal_call (GDK_WIN32_MODAL_OP_DND);
- hr = DoDragDrop (&dobj->ido, &pending_src_context->ids,
- DROPEFFECT_COPY | DROPEFFECT_MOVE,
- &dwEffect);
- _gdk_win32_end_modal_call (GDK_WIN32_MODAL_OP_DND);
+ _gdk_win32_begin_modal_call (GDK_WIN32_MODAL_OP_DND);
+ hr = DoDragDrop (&dobj->ido, &pending_src_context->ids,
+ DROPEFFECT_COPY | DROPEFFECT_MOVE,
+ &dwEffect);
+ _gdk_win32_end_modal_call (GDK_WIN32_MODAL_OP_DND);
- GDK_NOTE (DND, g_print ("DoDragDrop returned %s\n",
- (hr == DRAGDROP_S_DROP ? "DRAGDROP_S_DROP" :
- (hr == DRAGDROP_S_CANCEL ? "DRAGDROP_S_CANCEL" :
- (hr == E_UNEXPECTED ? "E_UNEXPECTED" :
- g_strdup_printf ("%#.8lx", hr))))));
+ GDK_NOTE (DND, g_print ("DoDragDrop returned %s\n",
+ (hr == DRAGDROP_S_DROP ? "DRAGDROP_S_DROP" :
+ (hr == DRAGDROP_S_CANCEL ? "DRAGDROP_S_CANCEL" :
+ (hr == E_UNEXPECTED ? "E_UNEXPECTED" :
+ g_strdup_printf ("%#.8lx", hr))))));
- /* Delete dnd selection after successful move */
- if (hr == DRAGDROP_S_DROP && dwEffect == DROPEFFECT_MOVE)
- {
- GdkEvent tmp_event;
-
- tmp_event.type = GDK_SELECTION_REQUEST;
- tmp_event.selection.window = drag_ctx->source_window;
- tmp_event.selection.send_event = FALSE;
- tmp_event.selection.selection = _gdk_ole2_dnd;
- tmp_event.selection.target = _delete;
- tmp_event.selection.property = _gdk_ole2_dnd; /* ??? */
- tmp_event.selection.time = GDK_CURRENT_TIME; /* ??? */
- g_object_ref (tmp_event.selection.window);
-
- GDK_NOTE (EVENTS, _gdk_win32_print_event (&tmp_event));
- gdk_event_put (&tmp_event);
- }
+ /* Delete dnd selection after successful move */
+ if (hr == DRAGDROP_S_DROP && dwEffect == DROPEFFECT_MOVE)
+ {
+ GdkEvent tmp_event;
+
+ memset (&tmp_event, 0, sizeof (tmp_event));
+ tmp_event.type = GDK_SELECTION_REQUEST;
+ g_set_object (&tmp_event.selection.window, drag_ctx->source_window);
+ tmp_event.selection.send_event = FALSE;
+ tmp_event.selection.selection = _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_OLE2_DND);
+ tmp_event.selection.target = _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_DELETE);
+ tmp_event.selection.property = _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_OLE2_DND);
+ g_set_object (&tmp_event.selection.requestor, drag_ctx->source_window);
+ tmp_event.selection.time = GDK_CURRENT_TIME; /* ??? */
+
+ GDK_NOTE (EVENTS, _gdk_win32_print_event (&tmp_event));
+ gdk_event_put (&tmp_event);
+ }
-#if 0
- // Send a GDK_DROP_FINISHED to the source window
- GetCursorPos (&pt);
- ptl.x = pt.x;
- ptl.y = pt.y;
- if ( pending_src_context != NULL && pending_src_context->context != NULL
- && pending_src_context->context->source_window != NULL )
- push_dnd_event (GDK_DROP_FINISHED, pending_src_context->context, ptl, FALSE);
-#endif
+ {
+ GdkEvent *tmp_event;
+ tmp_event = gdk_event_new (GDK_DROP_FINISHED);
+ g_set_object (&tmp_event->dnd.window, drag_ctx->source_window);
+ tmp_event->dnd.send_event = FALSE;
+ g_set_object (&tmp_event->dnd.context, drag_ctx);
+ gdk_event_set_device (tmp_event, gdk_drag_context_get_device (drag_ctx));
+ gdk_event_set_seat (tmp_event, gdk_device_get_seat (gdk_drag_context_get_device (drag_ctx)));
+
+ GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
+ gdk_event_put (tmp_event);
+ gdk_event_free (tmp_event);
+ }
- dobj->ido.lpVtbl->Release (&dobj->ido);
- if (pending_src_context != NULL)
- {
- pending_src_context->ids.lpVtbl->Release (&pending_src_context->ids);
- pending_src_context = NULL;
- }
+ current_src_object = NULL;
+ dobj->ido.lpVtbl->Release (&dobj->ido);
+ if (pending_src_context != NULL)
+ {
+ pending_src_context->ids.lpVtbl->Release (&pending_src_context->ids);
+ pending_src_context = NULL;
}
}
gint y_root,
GdkDragProtocol *protocol)
{
+ GdkWin32DragContext *context_win32 = GDK_WIN32_DRAG_CONTEXT (context);
GdkWindow *dest_window, *dw;
find_window_enum_arg a;
- a.x = x_root - _gdk_offset_x;
- a.y = y_root - _gdk_offset_y;
+ a.x = x_root * context_win32->scale - _gdk_offset_x;
+ a.y = y_root * context_win32->scale - _gdk_offset_y;
a.ignore = drag_window ? GDK_WINDOW_HWND (drag_window) : NULL;
a.result = NULL;
+ GDK_NOTE (DND,
+ g_print ("gdk_drag_find_window_real: %p %+d%+d\n",
+ (drag_window ? GDK_WINDOW_HWND (drag_window) : NULL),
+ a.x, a.y));
+
EnumWindows (find_window_enum_proc, (LPARAM) &a);
if (a.result == NULL)
context->actions = possible_actions;
- GDK_NOTE (DND, g_print ("gdk_drag_motion: %s suggested=%s, possible=%s\n"
+ GDK_NOTE (DND, g_print ("gdk_drag_motion: @ %+d:%+d %s suggested=%s, possible=%s\n"
" context=%p:{actions=%s,suggested=%s,action=%s}\n",
+ x_root, y_root,
_gdk_win32_drag_protocol_to_string (protocol),
_gdk_win32_drag_action_to_string (suggested_action),
_gdk_win32_drag_action_to_string (possible_actions),
context_win32 = GDK_WIN32_DRAG_CONTEXT (context);
+ if (context_win32->drag_window)
+ move_drag_window (context, x_root, y_root);
+
if (!use_ole2_dnd)
{
if (context->dest_window == dest_window)
/* Check if new destination accepts drags, and which protocol */
if (dest_window)
{
- context->dest_window = dest_window;
- g_object_ref (context->dest_window);
+ g_set_object (&context->dest_window, dest_window);
context->protocol = protocol;
switch (protocol)
/* Push a status event, to let the client know that
* the drag changed
*/
- tmp_event = gdk_event_new (GDK_DRAG_STATUS);
- tmp_event->dnd.window = g_object_ref (context->source_window);
+ tmp_event = gdk_event_new (GDK_DRAG_STATUS);
+ g_set_object (&tmp_event->dnd.window, context->source_window);
/* We use this to signal a synthetic status. Perhaps
* we should use an extra field...
*/
tmp_event->dnd.send_event = TRUE;
- tmp_event->dnd.context = g_object_ref (context);
+ g_set_object (&tmp_event->dnd.context, context);
tmp_event->dnd.time = time;
- gdk_event_set_device (tmp_event, gdk_drag_context_get_device (context));
- gdk_event_set_seat (tmp_event, gdk_device_get_seat (gdk_drag_context_get_device (context)));
+ gdk_event_set_device (tmp_event, gdk_drag_context_get_device (context));
+ gdk_event_set_seat (tmp_event, gdk_device_get_seat (gdk_drag_context_get_device (context)));
GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
gdk_event_put (tmp_event);
- gdk_event_free (tmp_event);
+ gdk_event_free (tmp_event);
}
/* Send a drag-motion event */
- context_win32->ole2_dnd_last_pt.x = x_root - _gdk_offset_x;
- context_win32->ole2_dnd_last_pt.y = y_root - _gdk_offset_y;
+ context_win32->last_x = x_root;
+ context_win32->last_y = y_root;
if (context->dest_window)
{
gdk_win32_drag_context_drag_drop (GdkDragContext *context,
guint32 time)
{
+ GdkWin32Selection *sel_win32 = _gdk_win32_selection_get ();
+
g_return_if_fail (context != NULL);
GDK_NOTE (DND, g_print ("gdk_drag_drop\n"));
}
else
{
- _dnd_source_state = GDK_WIN32_DND_DROPPED;
+ sel_win32->dnd_source_state = GDK_WIN32_DND_DROPPED;
}
}
gdk_win32_drag_context_drag_abort (GdkDragContext *context,
guint32 time)
{
+ GdkWin32Selection *sel_win32 = _gdk_win32_selection_get ();
+
g_return_if_fail (context != NULL);
GDK_NOTE (DND, g_print ("gdk_drag_abort\n"));
if (use_ole2_dnd)
- _dnd_source_state = GDK_WIN32_DND_NONE;
+ sel_win32->dnd_source_state = GDK_WIN32_DND_NONE;
}
/* Destination side */
if (src_context)
{
- GdkWin32DragContext *src_context_win32 = GDK_WIN32_DRAG_CONTEXT (src_context);
+ GdkWin32DragContext *src_context_win32 = GDK_WIN32_DRAG_CONTEXT (src_context);
- if (src_context_win32->drag_status == GDK_DRAG_STATUS_MOTION_WAIT)
+ if (src_context_win32->drag_status == GDK_DRAG_STATUS_MOTION_WAIT)
src_context_win32->drag_status = GDK_DRAG_STATUS_DRAG;
tmp_event = gdk_event_new (GDK_DRAG_STATUS);
- tmp_event->dnd.window = g_object_ref (context->source_window);
+ g_set_object (&tmp_event->dnd.window, context->source_window);
tmp_event->dnd.send_event = FALSE;
- tmp_event->dnd.context = g_object_ref (src_context);
+ g_set_object (&tmp_event->dnd.context, src_context);
tmp_event->dnd.time = GDK_CURRENT_TIME; /* FIXME? */
- gdk_event_set_device (tmp_event, gdk_drag_context_get_device (src_context));
- gdk_event_set_seat (tmp_event, gdk_device_get_seat (gdk_drag_context_get_device (src_context)));
+ gdk_event_set_device (tmp_event, gdk_drag_context_get_device (src_context));
+ gdk_event_set_seat (tmp_event, gdk_device_get_seat (gdk_drag_context_get_device (src_context)));
if (action == GDK_ACTION_DEFAULT)
action = 0;
GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
gdk_event_put (tmp_event);
- gdk_event_free (tmp_event);
+ gdk_event_free (tmp_event);
}
}
}
{
GdkDragContext *src_context;
GdkEvent *tmp_event;
+ GdkWin32Selection *sel_win32 = _gdk_win32_selection_get ();
g_return_if_fail (context != NULL);
context->dest_window);
if (src_context)
{
- tmp_event = gdk_event_new (GDK_DROP_FINISHED);
- tmp_event->dnd.window = g_object_ref (src_context->source_window);
+ tmp_event = gdk_event_new (GDK_DROP_FINISHED);
+ g_set_object (&tmp_event->dnd.window, src_context->source_window);
tmp_event->dnd.send_event = FALSE;
- tmp_event->dnd.context = g_object_ref (src_context);
- gdk_event_set_device (tmp_event, gdk_drag_context_get_device (src_context));
- gdk_event_set_seat (tmp_event, gdk_device_get_seat (gdk_drag_context_get_device (src_context)));
+ g_set_object (&tmp_event->dnd.context, src_context);
+ gdk_event_set_device (tmp_event, gdk_drag_context_get_device (src_context));
+ gdk_event_set_seat (tmp_event, gdk_device_get_seat (gdk_drag_context_get_device (src_context)));
GDK_NOTE (EVENTS, _gdk_win32_print_event (tmp_event));
gdk_event_put (tmp_event);
- gdk_event_free (tmp_event);
+ gdk_event_free (tmp_event);
}
}
else
gdk_drag_do_leave (context, time);
if (success)
- _dnd_target_state = GDK_WIN32_DND_DROPPED;
+ sel_win32->dnd_target_state = GDK_WIN32_DND_DROPPED;
else
- _dnd_target_state = GDK_WIN32_DND_FAILED;
+ sel_win32->dnd_target_state = GDK_WIN32_DND_FAILED;
}
}
if (g_hash_table_lookup (target_ctx_for_window, GDK_WINDOW_HWND (window)) != NULL)
return;
- /* Register for OLE2 d&d : similarly, claim to accept all supported
- * data types because we cannot know from here what the window
- * actually accepts.
- */
- /* FIXME: This of course won't work with user-extensible data types! */
ctx = target_context_new (window);
hr = CoLockObjectExternal ((IUnknown *) &ctx->idt, TRUE, FALSE);
switch (context->protocol)
{
case GDK_DRAG_PROTO_LOCAL:
- return _local_dnd;
+ return _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_LOCAL_DND_SELECTION);
case GDK_DRAG_PROTO_WIN32_DROPFILES:
- return _gdk_win32_dropfiles;
+ return _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_DROPFILES_DND);
case GDK_DRAG_PROTO_OLE2:
- return _gdk_ole2_dnd;
+ return _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_OLE2_DND);
default:
return GDK_NONE;
}
}
+static void
+gdk_win32_drag_context_set_cursor (GdkDragContext *context,
+ GdkCursor *cursor)
+{
+ GdkWin32DragContext *context_win32 = GDK_WIN32_DRAG_CONTEXT (context);
+
+ GDK_NOTE (DND, g_print ("gdk_drag_context_set_cursor: 0x%p 0x%p\n", context, cursor));
+
+ if (!g_set_object (&context_win32->cursor, cursor))
+ return;
+
+ if (context_win32->grab_seat)
+ {
+ G_GNUC_BEGIN_IGNORE_DEPRECATIONS;
+ gdk_device_grab (gdk_seat_get_pointer (context_win32->grab_seat),
+ context_win32->ipc_window,
+ GDK_OWNERSHIP_APPLICATION, FALSE,
+ GDK_POINTER_MOTION_MASK | GDK_BUTTON_RELEASE_MASK,
+ cursor, GDK_CURRENT_TIME);
+ G_GNUC_END_IGNORE_DEPRECATIONS;
+ }
+}
+
+static double
+ease_out_cubic (double t)
+{
+ double p = t - 1;
+ return p * p * p + 1;
+}
+
+#define ANIM_TIME 500000 /* half a second */
+
+typedef struct _GdkDragAnim GdkDragAnim;
+struct _GdkDragAnim {
+ GdkWin32DragContext *context;
+ GdkFrameClock *frame_clock;
+ gint64 start_time;
+};
+
+static void
+gdk_drag_anim_destroy (GdkDragAnim *anim)
+{
+ g_object_unref (anim->context);
+ g_slice_free (GdkDragAnim, anim);
+}
+
+static gboolean
+gdk_drag_anim_timeout (gpointer data)
+{
+ GdkDragAnim *anim = data;
+ GdkWin32DragContext *context = anim->context;
+ GdkFrameClock *frame_clock = anim->frame_clock;
+ gint64 current_time;
+ double f;
+ double t;
+
+ if (!frame_clock)
+ return G_SOURCE_REMOVE;
+
+ current_time = gdk_frame_clock_get_frame_time (frame_clock);
+
+ f = (current_time - anim->start_time) / (double) ANIM_TIME;
+
+ if (f >= 1.0)
+ return G_SOURCE_REMOVE;
+
+ t = ease_out_cubic (f);
+
+ gdk_window_show (context->drag_window);
+ gdk_window_move (context->drag_window,
+ context->last_x + (context->start_x - context->last_x) * t - context->hot_x,
+ сontext->last_y + (context->start_y - context->last_y) * t - context->hot_y);
+ gdk_window_set_opacity (context->drag_window, 1.0 - f);
+
+ return G_SOURCE_CONTINUE;
+}
+
+static void
+gdk_win32_drag_context_drop_done (GdkDragContext *context,
+ gboolean success)
+{
+ GdkWin32DragContext *win32_context = GDK_WIN32_DRAG_CONTEXT (context);
+ GdkDragAnim *anim;
+ cairo_surface_t *win_surface;
+ cairo_surface_t *surface;
+ cairo_pattern_t *pattern;
+ cairo_t *cr;
+
+ GDK_NOTE (DND, g_print ("gdk_drag_context_drop_done: 0x%p %s\n",
+ context,
+ success ? "dropped successfully" : "dropped unsuccessfully"));
+
+ if (success)
+ {
+ gdk_window_hide (win32_context->drag_window);
+ return;
+ }
+
+ win_surface = _gdk_window_ref_cairo_surface (win32_context->drag_window);
+ surface = gdk_window_create_similar_surface (win32_context->drag_window,
+ cairo_surface_get_content (win_surface),
+ gdk_window_get_width (win32_context->drag_window),
+ gdk_window_get_height (win32_context->drag_window));
+ cr = cairo_create (surface);
+ cairo_set_source_surface (cr, win_surface, 0, 0);
+ cairo_paint (cr);
+ cairo_destroy (cr);
+ cairo_surface_destroy (win_surface);
+
+ pattern = cairo_pattern_create_for_surface (surface);
+
+G_GNUC_BEGIN_IGNORE_DEPRECATIONS
+ gdk_window_set_background_pattern (win32_context->drag_window, pattern);
+G_GNUC_END_IGNORE_DEPRECATIONS
+
+ cairo_pattern_destroy (pattern);
+ cairo_surface_destroy (surface);
+
+ anim = g_slice_new0 (GdkDragAnim);
+ g_set_object (&anim->context, win32_context);
+ anim->frame_clock = gdk_window_get_frame_clock (win32_context->drag_window);
+ anim->start_time = gdk_frame_clock_get_frame_time (anim->frame_clock);
+
+ gdk_threads_add_timeout_full (G_PRIORITY_DEFAULT, 17,
+ gdk_drag_anim_timeout, anim,
+ (GDestroyNotify) gdk_drag_anim_destroy);
+}
+
+static gboolean
+drag_context_grab (GdkDragContext *context)
+{
+ GdkWin32DragContext *context_win32 = GDK_WIN32_DRAG_CONTEXT (context);
+ GdkSeatCapabilities capabilities;
+ GdkSeat *seat;
+ GdkCursor *cursor;
+
+ if (!context_win32->ipc_window)
+ return FALSE;
+
+ seat = gdk_device_get_seat (gdk_drag_context_get_device (context));
+
+ capabilities = GDK_SEAT_CAPABILITY_ALL;
+
+ cursor = gdk_drag_get_cursor (context, gdk_drag_context_get_selected_action (context));
+ g_set_object (&context_win32->cursor, cursor);
+
+ if (gdk_seat_grab (seat, context_win32->ipc_window,
+ capabilities, FALSE,
+ context_win32->cursor, NULL, NULL, NULL) != GDK_GRAB_SUCCESS)
+ return FALSE;
+
+ g_set_object (&context_win32->grab_seat, seat);
+
+ /* TODO: Should be grabbing keys here, to support keynav. SetWindowsHookEx()? */
+
+ return TRUE;
+}
+
+static void
+drag_context_ungrab (GdkDragContext *context)
+{
+ GdkWin32DragContext *context_win32 = GDK_WIN32_DRAG_CONTEXT (context);
+
+ if (!context_win32->grab_seat)
+ return;
+
+ gdk_seat_ungrab (context_win32->grab_seat);
+
+ g_clear_object (&context_win32->grab_seat);
+
+ /* TODO: Should be ungrabbing keys here */
+}
+
+static gboolean
+gdk_win32_drag_context_manage_dnd (GdkDragContext *context,
+ GdkWindow *ipc_window,
+ GdkDragAction actions)
+{
+ GdkWin32DragContext *context_win32 = GDK_WIN32_DRAG_CONTEXT (context);
+
+ if (context_win32->ipc_window)
+ return FALSE;
+
+ if (use_ole2_dnd)
+ context->protocol = GDK_DRAG_PROTO_OLE2;
+ else
+ context->protocol = GDK_DRAG_PROTO_LOCAL;
+
+ g_set_object (&context_win32->ipc_window, ipc_window);
+
+ if (drag_context_grab (context))
+ {
+ context_win32->actions = actions;
+ move_drag_window (context, context_win32->start_x, context_win32->start_y);
+ return TRUE;
+ }
+ else
+ {
+ g_clear_object (&context_win32->ipc_window);
+ return FALSE;
+ }
+}
+
+static void
+gdk_win32_drag_context_cancel (GdkDragContext *context,
+ GdkDragCancelReason reason)
+{
+ const gchar *reason_str = NULL;
+ switch (reason)
+ {
+ case GDK_DRAG_CANCEL_NO_TARGET:
+ reason_str = "no target";
+ break;
+ case GDK_DRAG_CANCEL_USER_CANCELLED:
+ reason_str = "user cancelled";
+ break;
+ case GDK_DRAG_CANCEL_ERROR:
+ reason_str = "error";
+ break;
+ default:
+ reason_str = "<unknown>";
+ break;
+ }
+
+ GDK_NOTE (DND, g_print ("gdk_drag_context_cancel: 0x%p %s\n",
+ context,
+ reason_str));
+ drag_context_ungrab (context);
+ gdk_drag_drop_done (context, FALSE);
+}
+
+static void
+gdk_win32_drag_context_drop_performed (GdkDragContext *context,
+ guint32 time_)
+{
+ GDK_NOTE (DND, g_print ("gdk_drag_context_drop_performed: 0x%p %u\n",
+ context,
+ time_));
+ gdk_drag_drop (context, time_);
+ drag_context_ungrab (context);
+}
+
+#define BIG_STEP 20
+#define SMALL_STEP 1
+
+static void
+gdk_drag_get_current_actions (GdkModifierType state,
+ gint button,
+ GdkDragAction actions,
+ GdkDragAction *suggested_action,
+ GdkDragAction *possible_actions)
+{
+ *suggested_action = 0;
+ *possible_actions = 0;
+
+ if ((button == GDK_BUTTON_MIDDLE || button == GDK_BUTTON_SECONDARY) && (actions & GDK_ACTION_ASK))
+ {
+ *suggested_action = GDK_ACTION_ASK;
+ *possible_actions = actions;
+ }
+ else if (state & (GDK_SHIFT_MASK | GDK_CONTROL_MASK))
+ {
+ if ((state & GDK_SHIFT_MASK) && (state & GDK_CONTROL_MASK))
+ {
+ if (actions & GDK_ACTION_LINK)
+ {
+ *suggested_action = GDK_ACTION_LINK;
+ *possible_actions = GDK_ACTION_LINK;
+ }
+ }
+ else if (state & GDK_CONTROL_MASK)
+ {
+ if (actions & GDK_ACTION_COPY)
+ {
+ *suggested_action = GDK_ACTION_COPY;
+ *possible_actions = GDK_ACTION_COPY;
+ }
+ }
+ else
+ {
+ if (actions & GDK_ACTION_MOVE)
+ {
+ *suggested_action = GDK_ACTION_MOVE;
+ *possible_actions = GDK_ACTION_MOVE;
+ }
+ }
+ }
+ else
+ {
+ *possible_actions = actions;
+
+ if ((state & (GDK_MOD1_MASK)) && (actions & GDK_ACTION_ASK))
+ *suggested_action = GDK_ACTION_ASK;
+ else if (actions & GDK_ACTION_COPY)
+ *suggested_action = GDK_ACTION_COPY;
+ else if (actions & GDK_ACTION_MOVE)
+ *suggested_action = GDK_ACTION_MOVE;
+ else if (actions & GDK_ACTION_LINK)
+ *suggested_action = GDK_ACTION_LINK;
+ }
+}
+
+static void
+gdk_drag_update (GdkDragContext *context,
+ gdouble x_root,
+ gdouble y_root,
+ GdkModifierType mods,
+ guint32 evtime)
+{
+ GdkWin32DragContext *win32_context = GDK_WIN32_DRAG_CONTEXT (context);
+ GdkDragAction action, possible_actions;
+ GdkWindow *dest_window;
+ GdkDragProtocol protocol;
+
+ gdk_drag_get_current_actions (mods, GDK_BUTTON_PRIMARY, win32_context->actions,
+ &action, &possible_actions);
+
+ gdk_drag_find_window_for_screen (context,
+ win32_context->drag_window,
+ gdk_display_get_default_screen (gdk_display_get_default ()),
+ x_root, y_root, &dest_window, &protocol);
+
+ gdk_drag_motion (context, dest_window, protocol, x_root, y_root,
+ action, possible_actions, evtime);
+}
+
+static gboolean
+gdk_dnd_handle_motion_event (GdkDragContext *context,
+ const GdkEventMotion *event)
+{
+ GdkModifierType state;
+
+ if (!gdk_event_get_state ((GdkEvent *) event, &state))
+ return FALSE;
+
+ GDK_NOTE (DND, g_print ("gd_dnd_handle_motion_event: 0x%p\n",
+ context));
+
+ gdk_drag_update (context, event->x_root, event->y_root, state,
+ gdk_event_get_time ((GdkEvent *) event));
+ return TRUE;
+}
+
+static gboolean
+gdk_dnd_handle_key_event (GdkDragContext *context,
+ const GdkEventKey *event)
+{
+ GdkWin32DragContext *win32_context = GDK_WIN32_DRAG_CONTEXT (context);
+ GdkModifierType state;
+ GdkWindow *root_window;
+ GdkDevice *pointer;
+ gint dx, dy;
+
+ GDK_NOTE (DND, g_print ("gdk_dnd_handle_key_event: 0x%p\n",
+ context));
+
+ dx = dy = 0;
+ state = event->state;
+ pointer = gdk_device_get_associated_device (gdk_event_get_device ((GdkEvent *) event));
+
+ if (event->type == GDK_KEY_PRESS)
+ {
+ switch (event->keyval)
+ {
+ case GDK_KEY_Escape:
+ gdk_drag_context_cancel (context, GDK_DRAG_CANCEL_USER_CANCELLED);
+ return TRUE;
+
+ case GDK_KEY_space:
+ case GDK_KEY_Return:
+ case GDK_KEY_ISO_Enter:
+ case GDK_KEY_KP_Enter:
+ case GDK_KEY_KP_Space:
+ if ((gdk_drag_context_get_selected_action (context) != 0) &&
+ (gdk_drag_context_get_dest_window (context) != NULL))
+ {
+ g_signal_emit_by_name (context, "drop-performed",
+ gdk_event_get_time ((GdkEvent *) event));
+ }
+ else
+ gdk_drag_context_cancel (context, GDK_DRAG_CANCEL_NO_TARGET);
+
+ return TRUE;
+
+ case GDK_KEY_Up:
+ case GDK_KEY_KP_Up:
+ dy = (state & GDK_MOD1_MASK) ? -BIG_STEP : -SMALL_STEP;
+ break;
+
+ case GDK_KEY_Down:
+ case GDK_KEY_KP_Down:
+ dy = (state & GDK_MOD1_MASK) ? BIG_STEP : SMALL_STEP;
+ break;
+
+ case GDK_KEY_Left:
+ case GDK_KEY_KP_Left:
+ dx = (state & GDK_MOD1_MASK) ? -BIG_STEP : -SMALL_STEP;
+ break;
+
+ case GDK_KEY_Right:
+ case GDK_KEY_KP_Right:
+ dx = (state & GDK_MOD1_MASK) ? BIG_STEP : SMALL_STEP;
+ break;
+ }
+ }
+
+ /* The state is not yet updated in the event, so we need
+ * to query it here.
+ */
+ root_window = gdk_screen_get_root_window (gdk_window_get_screen (win32_context->ipc_window));
+ gdk_window_get_device_position (root_window, pointer, NULL, NULL, &state);
+
+ if (dx != 0 || dy != 0)
+ {
+ win32_context->last_x += dx;
+ win32_context->last_y += dy;
+ gdk_device_warp (pointer,
+ gdk_window_get_screen (win32_context->ipc_window),
+ win32_context->last_x, win32_context->last_y);
+ }
+
+ gdk_drag_update (context, win32_context->last_x, win32_context->last_y, state,
+ gdk_event_get_time ((GdkEvent *) event));
+
+ return TRUE;
+}
+
+static gboolean
+gdk_dnd_handle_grab_broken_event (GdkDragContext *context,
+ const GdkEventGrabBroken *event)
+{
+ GdkWin32DragContext *win32_context = GDK_WIN32_DRAG_CONTEXT (context);
+
+ GDK_NOTE (DND, g_print ("gdk_dnd_handle_grab_broken_event: 0x%p\n",
+ context));
+
+ /* Don't cancel if we break the implicit grab from the initial button_press.
+ * Also, don't cancel if we re-grab on the widget or on our IPC window, for
+ * example, when changing the drag cursor.
+ */
+ if (event->implicit ||
+ event->grab_window == win32_context->drag_window ||
+ event->grab_window == win32_context->ipc_window)
+ return FALSE;
+
+ if (gdk_event_get_device ((GdkEvent *) event) !=
+ gdk_drag_context_get_device (context))
+ return FALSE;
+
+ gdk_drag_context_cancel (context, GDK_DRAG_CANCEL_ERROR);
+ return TRUE;
+}
+
+static gboolean
+gdk_dnd_handle_button_event (GdkDragContext *context,
+ const GdkEventButton *event)
+{
+ GDK_NOTE (DND, g_print ("gdk_dnd_handle_button_event: 0x%p\n",
+ context));
+
+#if 0
+ /* FIXME: Check the button matches */
+ if (event->button != win32_context->button)
+ return FALSE;
+#endif
+
+ if ((gdk_drag_context_get_selected_action (context) != 0) &&
+ (gdk_drag_context_get_dest_window (context) != NULL))
+ {
+ g_signal_emit_by_name (context, "drop-performed",
+ gdk_event_get_time ((GdkEvent *) event));
+ }
+ else
+ gdk_drag_context_cancel (context, GDK_DRAG_CANCEL_NO_TARGET);
+
+ return TRUE;
+}
+
+gboolean
+gdk_dnd_handle_drag_status (GdkDragContext *context,
+ const GdkEventDND *event)
+{
+ GdkWin32DragContext *context_win32 = GDK_WIN32_DRAG_CONTEXT (context);
+ GdkDragAction action;
+
+ GDK_NOTE (DND, g_print ("gdk_dnd_handle_drag_status: 0x%p\n",
+ context));
+
+ if (context != event->context)
+ return FALSE;
+
+ action = gdk_drag_context_get_selected_action (context);
+
+ if (action != context_win32->current_action)
+ {
+ context_win32->current_action = action;
+ g_signal_emit_by_name (context, "action-changed", action);
+ }
+
+ return TRUE;
+}
+
+static gboolean
+gdk_dnd_handle_drop_finished (GdkDragContext *context,
+ const GdkEventDND *event)
+{
+ GdkWin32DragContext *win32_context = GDK_WIN32_DRAG_CONTEXT (context);
+
+ GDK_NOTE (DND, g_print ("gdk_dnd_handle_drop_finihsed: 0x%p\n",
+ context));
+
+ if (context != event->context)
+ return FALSE;
+
+ g_signal_emit_by_name (context, "dnd-finished");
+ gdk_drag_drop_done (context, !win32_context->drop_failed);
+ gdk_win32_selection_clear_targets (gdk_display_get_default (),
+ gdk_win32_drag_context_get_selection (context));
+
+ return TRUE;
+}
+
+gboolean
+gdk_win32_drag_context_handle_event (GdkDragContext *context,
+ const GdkEvent *event)
+{
+ GdkWin32DragContext *win32_context = GDK_WIN32_DRAG_CONTEXT (context);
+
+ if (!context->is_source)
+ return FALSE;
+ if (!win32_context->grab_seat && event->type != GDK_DROP_FINISHED)
+ return FALSE;
+
+ switch (event->type)
+ {
+ case GDK_MOTION_NOTIFY:
+ return gdk_dnd_handle_motion_event (context, &event->motion);
+ case GDK_BUTTON_RELEASE:
+ return gdk_dnd_handle_button_event (context, &event->button);
+ case GDK_KEY_PRESS:
+ case GDK_KEY_RELEASE:
+ return gdk_dnd_handle_key_event (context, &event->key);
+ case GDK_GRAB_BROKEN:
+ return gdk_dnd_handle_grab_broken_event (context, &event->grab_broken);
+ case GDK_DRAG_STATUS:
+ return gdk_dnd_handle_drag_status (context, &event->dnd);
+ case GDK_DROP_FINISHED:
+ return gdk_dnd_handle_drop_finished (context, &event->dnd);
+ default:
+ break;
+ }
+
+ return FALSE;
+}
+
+void
+gdk_win32_drag_context_action_changed (GdkDragContext *context,
+ GdkDragAction action)
+{
+ GdkCursor *cursor;
+
+ cursor = gdk_drag_get_cursor (context, action);
+ gdk_drag_context_set_cursor (context, cursor);
+}
+
+static GdkWindow *
+gdk_win32_drag_context_get_drag_window (GdkDragContext *context)
+{
+ return GDK_WIN32_DRAG_CONTEXT (context)->drag_window;
+}
+
+static void
+gdk_win32_drag_context_set_hotspot (GdkDragContext *context,
+ gint hot_x,
+ gint hot_y)
+{
+ GdkWin32DragContext *win32_context = GDK_WIN32_DRAG_CONTEXT (context);
+
+ GDK_NOTE (DND, g_print ("gdk_drag_context_set_hotspot: 0x%p %d:%d\n",
+ context,
+ hot_x, hot_y));
+
+ win32_context->hot_x = hot_x;
+ win32_context->hot_y = hot_y;
+
+ if (win32_context->grab_seat)
+ {
+ /* DnD is managed, update current position */
+ move_drag_window (context, win32_context->last_x, win32_context->last_y);
+ }
+}
+
static void
gdk_win32_drag_context_class_init (GdkWin32DragContextClass *klass)
{
context_class->drop_finish = gdk_win32_drag_context_drop_finish;
context_class->drop_status = gdk_win32_drag_context_drop_status;
context_class->get_selection = gdk_win32_drag_context_get_selection;
+
+ context_class->get_drag_window = gdk_win32_drag_context_get_drag_window;
+ context_class->set_hotspot = gdk_win32_drag_context_set_hotspot;
+ context_class->drop_done = gdk_win32_drag_context_drop_done;
+ context_class->manage_dnd = gdk_win32_drag_context_manage_dnd;
+ context_class->set_cursor = gdk_win32_drag_context_set_cursor;
+ context_class->cancel = gdk_win32_drag_context_cancel;
+ context_class->drop_performed = gdk_win32_drag_context_drop_performed;
+ context_class->handle_event = gdk_win32_drag_context_handle_event;
+ context_class->action_changed = gdk_win32_drag_context_action_changed;
+
}
#include "gdkdevice-wintab.h"
#include "gdkwin32dnd.h"
#include "gdkdisplay-win32.h"
+#include "gdkselection-win32.h"
#include "gdkdndprivate.h"
#include <windowsx.h>
int i;
+ GdkWin32Selection *win32_sel = NULL;
+
+ STGMEDIUM *property_change_data;
+
display = gdk_display_get_default ();
window = gdk_win32_handle_table_lookup (msg->hwnd);
case WM_KILLFOCUS:
if (keyboard_grab != NULL &&
- !GDK_WINDOW_DESTROYED (keyboard_grab->window))
+ !GDK_WINDOW_DESTROYED (keyboard_grab->window) &&
+ (_modal_operation_in_progress & GDK_WIN32_MODAL_OP_DND) == 0)
{
generate_grab_broken_event (device_manager, keyboard_grab->window, TRUE, NULL);
}
break;
case WM_DESTROYCLIPBOARD:
- if (!_ignore_destroy_clipboard)
+ win32_sel = _gdk_win32_selection_get ();
+
+ if (!win32_sel->ignore_destroy_clipboard)
{
event = gdk_event_new (GDK_SELECTION_CLEAR);
event->selection.window = window;
case WM_RENDERFORMAT:
GDK_NOTE (EVENTS, g_print (" %s", _gdk_win32_cf_to_string (msg->wParam)));
- if (!(target = g_hash_table_lookup (_format_atom_table, GINT_TO_POINTER (msg->wParam))))
- {
- GDK_NOTE (EVENTS, g_print (" (target not found)"));
- return_val = TRUE;
- break;
- }
+ *ret_valp = 0;
+ return_val = TRUE;
+
+ win32_sel = _gdk_win32_selection_get ();
+
+ for (target = NULL, i = 0;
+ i < win32_sel->clipboard_selection_targets->len;
+ i++)
+ {
+ GdkSelTargetFormat target_format = g_array_index (win32_sel->clipboard_selection_targets, GdkSelTargetFormat, i);
+
+ if (target_format.format == msg->wParam)
+ {
+ target = target_format.target;
+ win32_sel->property_change_transmute = target_format.transmute;
+ }
+ }
+
+ if (target == NULL)
+ {
+ GDK_NOTE (EVENTS, g_print (" (target not found)"));
+ break;
+ }
/* We need to render to clipboard immediately, don't call
* _gdk_win32_append_event()
event->selection.send_event = FALSE;
event->selection.selection = GDK_SELECTION_CLIPBOARD;
event->selection.target = target;
- event->selection.property = _gdk_selection;
+ event->selection.property = _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_GDK_SELECTION);
event->selection.requestor = gdk_win32_handle_table_lookup (msg->hwnd);
event->selection.time = msg->time;
+ property_change_data = g_new0 (STGMEDIUM, 1);
+ win32_sel->property_change_data = property_change_data;
+ win32_sel->property_change_format = msg->wParam;
fixup_event (event);
GDK_NOTE (EVENTS, g_print (" (calling _gdk_event_emit)"));
GDK_NOTE (EVENTS, _gdk_win32_print_event (event));
_gdk_event_emit (event);
gdk_event_free (event);
+ win32_sel->property_change_format = 0;
/* Now the clipboard owner should have rendered */
- if (!_delayed_rendering_data)
+ if (!property_change_data->hGlobal)
{
GDK_NOTE (EVENTS, g_print (" (no _delayed_rendering_data?)"));
}
else
{
- if (msg->wParam == CF_DIB)
- {
- _delayed_rendering_data =
- _gdk_win32_selection_convert_to_dib (_delayed_rendering_data,
- target);
- if (!_delayed_rendering_data)
- {
- g_warning ("Cannot convert to DIB from delayed rendered image");
- break;
- }
- }
-
/* The requestor is holding the clipboard, no
* OpenClipboard() is required/possible
*/
GDK_NOTE (DND,
g_print (" SetClipboardData(%s,%p)",
_gdk_win32_cf_to_string (msg->wParam),
- _delayed_rendering_data));
+ property_change_data->hGlobal));
- API_CALL (SetClipboardData, (msg->wParam, _delayed_rendering_data));
- _delayed_rendering_data = NULL;
+ API_CALL (SetClipboardData, (msg->wParam, property_change_data->hGlobal));
+ }
+
+ g_clear_pointer (&property_change_data, g_free);
+ *ret_valp = 0;
+ return_val = TRUE;
+ break;
+
+ case WM_RENDERALLFORMATS:
+ *ret_valp = 0;
+ return_val = TRUE;
+
+ win32_sel = _gdk_win32_selection_get ();
+
+ if (API_CALL (OpenClipboard, (msg->hwnd)))
+ {
+ for (target = NULL, i = 0;
+ i < win32_sel->clipboard_selection_targets->len;
+ i++)
+ {
+ GdkSelTargetFormat target_format = g_array_index (win32_sel->clipboard_selection_targets, GdkSelTargetFormat, i);
+ if (target_format.format != 0)
+ SendMessage (msg->hwnd, WM_RENDERFORMAT, target_format.format, 0);
+ }
+
+ API_CALL (CloseClipboard, ());
}
break;
if (event)
{
+ GdkWin32Selection *sel_win32 = _gdk_win32_selection_get ();
+
_gdk_event_emit (event);
gdk_event_free (event);
/* Do drag & drop if it is still pending */
- if (_dnd_source_state == GDK_WIN32_DND_PENDING)
+ if (sel_win32->dnd_source_state == GDK_WIN32_DND_PENDING)
{
- _dnd_source_state = GDK_WIN32_DND_DRAGGING;
+ sel_win32->dnd_source_state = GDK_WIN32_DND_DRAGGING;
_gdk_win32_dnd_do_dragdrop ();
- _dnd_source_state = GDK_WIN32_DND_NONE;
+ sel_win32->dnd_source_state = GDK_WIN32_DND_NONE;
}
}
gboolean _gdk_input_locale_is_ime;
UINT _gdk_input_codepage;
-GdkAtom _gdk_selection;
-GdkAtom _wm_transient_for;
-GdkAtom _targets;
-GdkAtom _delete;
-GdkAtom _save_targets;
-GdkAtom _utf8_string;
-GdkAtom _text;
-GdkAtom _compound_text;
-GdkAtom _text_uri_list;
-GdkAtom _text_html;
-GdkAtom _image_png;
-GdkAtom _image_jpeg;
-GdkAtom _image_bmp;
-GdkAtom _image_gif;
-
-GdkAtom _local_dnd;
-GdkAtom _gdk_win32_dropfiles;
-GdkAtom _gdk_ole2_dnd;
-
-UINT _cf_png;
-UINT _cf_jfif;
-UINT _cf_gif;
-UINT _cf_url;
-UINT _cf_html_format;
-UINT _cf_text_html;
-
-GdkWin32DndState _dnd_target_state = GDK_WIN32_DND_NONE;
-GdkWin32DndState _dnd_source_state = GDK_WIN32_DND_NONE;
-
gint _gdk_input_ignore_wintab = FALSE;
gint _gdk_max_colors = 0;
GdkWin32ModalOpKind _modal_operation_in_progress = GDK_WIN32_MODAL_OP_NONE;
HWND _modal_move_resize_window = NULL;
-gboolean _ignore_destroy_clipboard = FALSE;
-HGLOBAL _delayed_rendering_data = NULL;
-GHashTable *_format_atom_table = NULL;
+/* The singleton selection object pointer */
+GdkWin32Selection *_win32_selection = NULL;
#include <wintab.h>
#include <imm.h>
+/* for CFSTR_SHELLIDLIST */
+#include <shlobj.h>
+
static gboolean gdk_synchronize = FALSE;
static gboolean dummy;
GDK_NOTE (EVENTS, g_print ("input_locale:%p, codepage:%d\n",
_gdk_input_locale, _gdk_input_codepage));
- _gdk_selection = gdk_atom_intern_static_string ("GDK_SELECTION");
- _wm_transient_for = gdk_atom_intern_static_string ("WM_TRANSIENT_FOR");
- _targets = gdk_atom_intern_static_string ("TARGETS");
- _delete = gdk_atom_intern_static_string ("DELETE");
- _save_targets = gdk_atom_intern_static_string ("SAVE_TARGETS");
- _utf8_string = gdk_atom_intern_static_string ("UTF8_STRING");
- _text = gdk_atom_intern_static_string ("TEXT");
- _compound_text = gdk_atom_intern_static_string ("COMPOUND_TEXT");
- _text_uri_list = gdk_atom_intern_static_string ("text/uri-list");
- _text_html = gdk_atom_intern_static_string ("text/html");
- _image_png = gdk_atom_intern_static_string ("image/png");
- _image_jpeg = gdk_atom_intern_static_string ("image/jpeg");
- _image_bmp = gdk_atom_intern_static_string ("image/bmp");
- _image_gif = gdk_atom_intern_static_string ("image/gif");
-
- _local_dnd = gdk_atom_intern_static_string ("LocalDndSelection");
- _gdk_win32_dropfiles = gdk_atom_intern_static_string ("DROPFILES_DND");
- _gdk_ole2_dnd = gdk_atom_intern_static_string ("OLE2_DND");
-
- /* MS Office 2007, at least, offers images in common file formats
- * using clipboard format names like "PNG" and "JFIF". So we follow
- * the lead and map the GDK target name "image/png" to the clipboard
- * format name "PNG" etc.
- */
- _cf_png = RegisterClipboardFormat ("PNG");
- _cf_jfif = RegisterClipboardFormat ("JFIF");
- _cf_gif = RegisterClipboardFormat ("GIF");
-
- _cf_url = RegisterClipboardFormat ("UniformResourceLocatorW");
- _cf_html_format = RegisterClipboardFormat ("HTML Format");
- _cf_text_html = RegisterClipboardFormat ("text/html");
-
_gdk_win32_selection_init ();
}
#include <gdk/win32/gdkwin32display.h>
#include <gdk/win32/gdkwin32screen.h>
#include <gdk/win32/gdkwin32keys.h>
+#include <gdk/win32/gdkselection-win32.h>
#include "gdkinternals.h"
extern guint _gdk_keymap_serial;
-/* GdkAtoms: properties, targets and types */
-extern GdkAtom _gdk_selection;
-extern GdkAtom _wm_transient_for;
-extern GdkAtom _targets;
-extern GdkAtom _delete;
-extern GdkAtom _save_targets;
-extern GdkAtom _utf8_string;
-extern GdkAtom _text;
-extern GdkAtom _compound_text;
-extern GdkAtom _text_uri_list;
-extern GdkAtom _text_html;
-extern GdkAtom _image_png;
-extern GdkAtom _image_jpeg;
-extern GdkAtom _image_bmp;
-extern GdkAtom _image_gif;
-
-/* DND selections */
-extern GdkAtom _local_dnd;
-extern GdkAtom _gdk_win32_dropfiles;
-extern GdkAtom _gdk_ole2_dnd;
-
-/* Clipboard formats */
-extern UINT _cf_png;
-extern UINT _cf_jfif;
-extern UINT _cf_gif;
-extern UINT _cf_url;
-extern UINT _cf_html_format;
-extern UINT _cf_text_html;
-
-/* OLE-based DND state */
-typedef enum {
- GDK_WIN32_DND_NONE,
- GDK_WIN32_DND_PENDING,
- GDK_WIN32_DND_DROPPED,
- GDK_WIN32_DND_FAILED,
- GDK_WIN32_DND_DRAGGING,
-} GdkWin32DndState;
-
-extern GdkWin32DndState _dnd_target_state;
-extern GdkWin32DndState _dnd_source_state;
+/* The singleton selection object pointer */
+GdkWin32Selection *_win32_selection;
void _gdk_win32_dnd_do_dragdrop (void);
void _gdk_win32_ole2_dnd_property_change (GdkAtom type,
#define GDK_WIN32_COLORMAP_DATA(cmap) ((GdkColormapPrivateWin32 *) GDK_COLORMAP (cmap)->windowing_data)
-/* TRUE when we are emptying the clipboard ourselves */
-extern gboolean _ignore_destroy_clipboard;
-
-/* Mapping from registered clipboard format id (native) to
- * corresponding GdkAtom
- */
-extern GHashTable *_format_atom_table;
-
-/* Hold the result of a delayed rendering */
-extern HGLOBAL _delayed_rendering_data;
-
extern GdkCursor *_gdk_win32_grab_cursor;
-HGLOBAL _gdk_win32_selection_convert_to_dib (HGLOBAL hdata,
- GdkAtom target);
-
/* Convert a pixbuf to an HICON (or HCURSOR). Supports alpha under
* Windows XP, thresholds alpha otherwise.
*/
}
void
-_gdk_win32_window_change_property (GdkWindow *window,
- GdkAtom property,
- GdkAtom type,
- gint format,
- GdkPropMode mode,
- const guchar *data,
- gint nelements)
+_gdk_win32_window_change_property (GdkWindow *window,
+ GdkAtom property,
+ GdkAtom type,
+ gint format,
+ GdkPropMode mode,
+ const guchar *data,
+ gint nelements)
{
- HGLOBAL hdata;
- gint i, size;
- guchar *ucptr;
- wchar_t *wcptr, *p;
- glong wclen;
- GError *err = NULL;
+ GdkWin32Selection *win32_sel = _gdk_win32_selection_get ();
g_return_if_fail (window != NULL);
g_return_if_fail (GDK_IS_WINDOW (window));
GDK_NOTE (DND, {
gchar *prop_name = gdk_atom_name (property);
gchar *type_name = gdk_atom_name (type);
+ gchar *datastring = _gdk_win32_data_to_string (data, MIN (10, format*nelements/8));
g_print ("gdk_property_change: %p %s %s %s %d*%d bits: %s\n",
GDK_WINDOW_HWND (window),
(mode == GDK_PROP_MODE_APPEND ? "APPEND" :
"???"))),
format, nelements,
- _gdk_win32_data_to_string (data, MIN (10, format*nelements/8)));
+ datastring);
+ g_free (datastring);
g_free (prop_name);
g_free (type_name);
});
+#ifndef G_DISABLE_CHECKS
/* We should never come here for these types */
- g_return_if_fail (type != GDK_TARGET_STRING);
- g_return_if_fail (type != _text);
- g_return_if_fail (type != _compound_text);
- g_return_if_fail (type != _save_targets);
-
- if (property == _gdk_selection &&
- format == 8 &&
- mode == GDK_PROP_MODE_REPLACE)
+ if (G_UNLIKELY (type == _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_COMPOUND_TEXT) ||
+ type == _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_SAVE_TARGETS)))
{
- if (type == _image_bmp && nelements < sizeof (BITMAPFILEHEADER))
- {
- g_warning ("Clipboard contains invalid bitmap data");
- return;
- }
-
- if (type == _utf8_string)
- {
- wcptr = g_utf8_to_utf16 ((char *) data, nelements, NULL, &wclen, &err);
- if (err != NULL)
- {
- g_warning ("Failed to convert utf8: %s", err->message);
- g_clear_error (&err);
- return;
- }
-
- if (!OpenClipboard (GDK_WINDOW_HWND (window)))
- {
- WIN32_API_FAILED ("OpenClipboard");
- g_free (wcptr);
- return;
- }
-
- wclen++; /* Terminating 0 */
- size = wclen * 2;
- for (i = 0; i < wclen; i++)
- if (wcptr[i] == '\n' && (i == 0 || wcptr[i - 1] != '\r'))
- size += 2;
-
- if (!(hdata = GlobalAlloc (GMEM_MOVEABLE, size)))
- {
- WIN32_API_FAILED ("GlobalAlloc");
- if (!CloseClipboard ())
- WIN32_API_FAILED ("CloseClipboard");
- g_free (wcptr);
- return;
- }
-
- ucptr = GlobalLock (hdata);
-
- p = (wchar_t *) ucptr;
- for (i = 0; i < wclen; i++)
- {
- if (wcptr[i] == '\n' && (i == 0 || wcptr[i - 1] != '\r'))
- *p++ = '\r';
- *p++ = wcptr[i];
- }
- g_free (wcptr);
-
- GlobalUnlock (hdata);
- GDK_NOTE (DND, g_print ("... SetClipboardData(CF_UNICODETEXT,%p)\n",
- hdata));
- if (!SetClipboardData (CF_UNICODETEXT, hdata))
- WIN32_API_FAILED ("SetClipboardData");
-
- if (!CloseClipboard ())
- WIN32_API_FAILED ("CloseClipboard");
- }
- else
- {
- /* We use delayed rendering for everything else than
- * text. We can't assign hdata to the clipboard here as type
- * may be "image/png", "image/jpg", etc. In this case
- * there's a further conversion afterwards.
- */
- GDK_NOTE (DND, g_print ("... delayed rendering\n"));
- _delayed_rendering_data = NULL;
- if (!(hdata = GlobalAlloc (GMEM_MOVEABLE, nelements > 0 ? nelements : 1)))
- {
- WIN32_API_FAILED ("GlobalAlloc");
- return;
- }
- ucptr = GlobalLock (hdata);
- memcpy (ucptr, data, nelements);
- GlobalUnlock (hdata);
- _delayed_rendering_data = hdata;
- }
+ g_return_if_fail_warning (G_LOG_DOMAIN,
+ G_STRFUNC,
+ "change_property called with a bad type");
+ return;
}
- else if (property == _gdk_ole2_dnd)
+#endif
+
+ if (property == _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_GDK_SELECTION) ||
+ property == _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_OLE2_DND))
{
- /* Will happen only if gdkdnd-win32.c has OLE2 dnd support compiled in */
- _gdk_win32_ole2_dnd_property_change (type, format, data, nelements);
+ _gdk_win32_selection_property_change (win32_sel,
+ window,
+ property,
+ type,
+ format,
+ mode,
+ data,
+ nelements);
}
else
g_warning ("gdk_property_change: General case not implemented");
g_free (prop_name);
});
- if (property == _gdk_selection)
+ if (property == _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_GDK_SELECTION) ||
+ property == _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_OLE2_DND))
_gdk_selection_property_delete (window);
- else if (property == _wm_transient_for)
+ else if (property == _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_WM_TRANSIENT_FOR))
{
GdkScreen *screen;
* GTK+ at ftp://ftp.gtk.org/pub/gtk/.
*/
+/*
+GTK+ selection works like this:
+There are three selections that matter - GDK_SELECTION_CLIPBOARD,
+GDK_SELECTION_PRIMARY and DND. Primary selection is only handled
+internally by GTK+ (it's not portable to Windows). DND is actually
+represented by two selections - LOCAL and OLE2, one for each DnD protocol,
+but they work the same way.
+
+"Target" is a GdkAtom describing a clipboard format.
+
+For Clipboard:
+GTK+ calls gtk_clipboard_set_contents(), which first ensures the
+clipboard is owned by the clipboard widget (which also indirectly
+causes an SelectionRequest xevent to be sent to it), then clears the old
+supported targets from the clipboard, then adds all the
+targets it's given to the clipboard. No data is sent anywhere.
+
+gtk_clipboard_set_contents() is also given a callback to invoke when
+the actual data is needed. This callback is implemented by the widget
+from which the data can be put into clipboard.
+
+GTK+ might also call gtk_clipboard_set_can_store(), which sets the
+targets for which the data can be put into system clipboard, so that
+it remains usable even if the application is no longer around. Usually
+all data formats are storable, except for the shortcut formats, which
+refer to actual widgets directly, and are thus only working while
+the application is alive.
+
+("C:" means clipboard client (requestor), "S:" means clipboard server (provider))
+
+When something needs to be obtained from clipboard, GTK+ calls
+C: gtk_selection_convert().
+That function has a shortcut where it directly gets the selection contents by calling
+S: gtk_selection_invoke_handler(),
+asking the widget to provide data, and then calling
+C: gtk_selection_retrieval_report()
+to report the data back to the caller.
+
+If that shortcut isn't possible (selection is owned by another process),
+gtk_selection_convert() calls
+C:gdk_selection_convert() (_gdk_x11_display_convert_selection())
+
+On X11 gdk_selection_convert() just calls
+C:XConvertSelection(),
+which sends SelectionRequest xevent to the window that owns the selection.
+The client gives its clipboard window as the requestor for that event,
+and gives the property as GDK_SELECTION.
+
+Server-side GTK+ catches SelectionRequest in a
+S:_gtk_selection_request()
+event handler, which calls
+S:gtk_selection_invoke_handler()
+to get the data, and then calls
+S:gdk_property_change() (_gdk_x11_window_change_property())
+to submit the data, by setting the property given by the message sender
+(GDK_SELECTION) on the requestor window (our client clipboard window).
+
+On X11 data submission takes from of
+S:XChangeProperty()
+call, which causes SelectionNotify (and PropertyNotify for INCR)
+xevent to be sent, which client-side GTK+ catches and handles in
+C:_gtk_selection_notify()
+(and
+C:_gtk_selection_property_notify(),
+for INCR)
+event handler, which calls
+C:gtk_selection_retrieval_report()
+to report back to the caller. The caller gets the property
+data from the window, and returns it up the stack.
+
+On X11 the "TARGETS" target might be given in a SelectionRequest xmessage to request
+all supported targets for a selection.
+
+If data must be stored on the clipboard, because the application is quitting,
+GTK+ will call
+S:gdk_clipboard_store() -> gdk_display_store_clipboard() (gdk_x11_display_store_clipboard())
+on all the clipboards it owns.
+X11 gdk_display_store_clipboard() puts a list of storeable targets into GDK_SELECTION
+property of the clipboard window, then calls
+S:XConvertSelection()
+on the clipboard manager window (retrieved from the CLIPBOARD_MANAGER atom),
+and the clipboard manager responds by requesting all these formats and storing the data,
+then responds with SelectionNotify xevent to allow the application to quit.
+
+When clipboard owner changes, the old owner receives SelectionClear xevent,
+GTK+ handles it by clearing the clipboard object on its own level, GDK
+is not involved.
+
+On Windows:
+Clipboard is opened by OpenClipboard(), emptied by EmptyClipboard() (which also
+makes the window the clipboard owner), data is put into it by SetClipboardData().
+Clipboard is closed with CloseClipboard().
+If SetClipboardData() is given a NULL data value, the owner will later
+receive WM_RENDERFORMAT message, in response to which it must call
+SetClipboardData() with the provided handle and the actual data this time.
+This way applications can avoid storing everything in the clipboard
+all the time, only putting the data there as it is requested by other applications.
+At some undefined points of time an application might get WM_RENDERALLFORMATS
+message, it should respond by opening the clipboard and rendering
+into it all the data that it offers, as if responding to multiple WM_RENDERFORMAT
+messages.
+
+On GDK-Win32:
+GTK+ calls gtk_clipboard_set_contents(), which first ensures the
+clipboard is owned by the clipboard widget (calls OpenClipboard(),
+then EmptyClipboard() to become the owner, then
+sends a TARGETS GDK_SELECTION_REQUEST to itself, without closing the clipboard),
+then clears the old supported targets from the clipboard, then adds all the
+targets it's given to the clipboard. No data is sent anywhere.
+
+gtk_clipboard_set_contents() is also given a callback to invoke when
+the actual data is needed. This callback is implemented by the widget
+from which the data can be put into clipboard.
+
+GTK+ might also call gtk_clipboard_set_can_store(), which sets the
+targets for which the data can be put into system clipboard, so that
+it remains usable even if the application is no longer around. Usually
+all data formats are storable, except for the shortcut formats, which
+refer to actual widgets directly, and are thus only working while
+the application is alive.
+
+("C:" means clipboard client (requestor), "S:" means clipboard server (provider))
+("transmute" here means "change the format of some data"; this term is used here
+ instead of "convert" to avoid clashing with g(t|d)k_selection_convert(), which
+ is completely unrelated)
+
+When something needs to be obtained from clipboard, GTK+ calls
+C: gtk_selection_convert().
+That function has a shortcut where it directly gets the selection contents by calling
+S: gtk_selection_invoke_handler(),
+asking the widget to provide data, and then calling
+C: gtk_selection_retrieval_report()
+to report the data back to the caller.
+
+If that shortcut isn't possible (selection is owned by another process),
+gtk_selection_convert() calls
+C:gdk_selection_convert() (_gdk_win32_display_convert_selection())
+
+On GDK-Win32 gdk_selection_convert() just calls
+C:OpenClipboard()
+to open clipboard (if that fails, it shedules a timeout to regularly
+try to open clipboard for the next 30 seconds, and do the actions
+outlined below once the clipboard is opened, or notify about
+conversion failure after 30 seconds),
+C:EnumClipboardFormats() (2000+)
+to get the list of supported formats, figures out the format it should
+use to request the data (first it looks for supported formats with names
+that match the target name, then looks through compatibility
+formats for the target and checks whether these are supported).
+Note that it has no list of supported targets at hand,
+just the single requested target, and thus it might have
+to do some transmutation between formats; the caller up the stack
+either only supports just one format that it asks for,
+or supports multiple formats and asks for them in sequence (from
+the most preferred to the least preferred), until one call succeeds,
+or supports multiple formats and asks for the TARGETS format first,
+and then figures out what to ask for - GDK can't know that.
+Either way, GDK has to call
+C:GetClipboardData()
+to get the data (this causes WM_RENDERFORMAT to be sent to the owner,
+if the owner uses delayed rendering for the requested format, otherwise
+it just picks the data right from the OS)
+
+Server-side GDK catches WM_RENDERFORMAT, figures out a target
+to request (this one is easier, as it has the list of supported
+targets saved up), and posts a GDK_SELECTION_REQUEST event, then runs the main loop,
+while GTK+ catches the event in a
+S:_gtk_selection_request()
+event handler, which calls
+S:gtk_selection_invoke_handler()
+to get the data, and then calls
+S:gdk_property_change() (_gdk_win32_window_change_property())
+to submit the data, by first transmuting it to the format actually requested
+by the sender of WM_RENDERFORMAT, and then by returning thedata back up the stack,
+to the WM_RENDERFORMAT handler, which then calls
+S:SetClipboardData()
+with the handle provided by the sender.
+
+Meanwhile, the client code, still in
+C:_gdk_win32_display_convert_selection(),
+gets the data in response to GetClipboardData(),
+transmutes it (if needed) to the target format, sets the requested
+window property to that data (unlike change_property!),
+calls
+C:CloseClipboard() (if there are no more clipboard opeartions
+scheduled)
+and posts a GDK_SELECTION_NOTIFY event, which GTK+ catches in
+C:_gtk_selection_notify()
+event handler, which calls
+C:gtk_selection_retrieval_report()
+to report back to the caller. The caller gets the property
+data from the window, and returns it up the stack.
+
+On GDK-Win32 the "TARGETS" target might be given in a GDK_SELECTION_REQUEST to request
+all supported targets for a selection.
+Note that this server side -
+client side should call gdk_selection_convert() -> gdk_selection_convert() with "TARGETS" target
+to get the list of targets offered by the clipboard holder. It never causes GDK_SELECTION_REQUEST
+to be generated, just queries the system clipboard.
+On server side GDK_SELECTION_REQUEST is only generated internally:
+in response to WM_RENDERFORMAT (it renders a target),
+in response to idataobject_getdata() (it renders a target),
+after DnD ends (with a DELETE target, this is caught by GTK to make it delete the selection),
+and in response to owner change, with TARGETS target, which makes it register its formats by calling
+S:SetClipboardData(..., NULL)
+
+If data must be stored on the clipboard, because the application is quitting,
+GTK+ will call
+S:gdk_clipboard_store() -> gdk_display_store_clipboard() (gdk_win32_display_store_clipboard())
+on all the clipboards it owns.
+GDK-Win32 gdk_display_store_clipboard() sends WM_RENDERALLFORMATS to the window,
+then posts a GDK_SELECTION_NOTIFY event allow the application to quit.
+
+When clipboard owner changes, the old owner receives WM_DESTROYCLIPBOARD message,
+GDK handles it by posting a GDK_SELECTION_CLEAR event, which
+GTK+ handles by clearing the clipboard object on its own level.
+
+Any operations that require OpenClipboard()/CloseClipboard() combo (i.e.
+everything, except for WM_RENDERFORMAT handling) must be put into a queue,
+and then a once-per-second-for-up-to-30-seconds timeout must be added.
+The timeout function must call OpenClipboard(),
+and then proceed to perform the queued actions on the clipboard, once it opened,
+or return and try again a second later, as long as there are still items in the queue,
+and remove the queue items that are older than 30 seconds.
+Once the queue is empty, the clipboard is closed.
+
+DND:
+GDK-Win32:
+S:idataobject_getdata()
+sends a GDK_SELECTION_REQUEST event, which results in a call to
+S:_gdk_win32_window_change_property()
+which passes clipboard data back via the selection singleton.
+GDK-Win32 uses delayed rendering for all formats, even text.
+
+GTK+ will call
+C:gtk_selection_convert() -> gdk_selection_convert() (_gdk_win32_display_convert_selection())
+to get the data associated with the drag, when GTK+ apps want to inspect the data,
+but with a OLE2_DND selection instead of CLIPBOARD selection.
+
+_gdk_win32_display_convert_selection() queries the droptarget global variable,
+which should already contain a matched list of supported formats and targets,
+picks a format there, then queries it from the IDataObject that the droptarget kept around.
+Then optionally transmutes the data, and sets the property. Then posts GDK_SELECTION_NOTIFY.
+
+GTK+ catches that event and processes it, causeing "selection-received" signal to
+be emitted on the selection widget, and its handler is
+C:gtk_drag_selection_received(),
+which emits the "drag-data-received" signal for the app.
+*/
+
#include "config.h"
#include <string.h>
#include <stdlib.h>
+/* For C-style COM wrapper macros */
+#define COBJMACROS
+
+/* for CIDA */
+#include <shlobj.h>
+
#include "gdkproperty.h"
#include "gdkselection.h"
#include "gdkdisplay.h"
#include "gdkprivate-win32.h"
+#include "gdkselection-win32.h"
+#include "gdk/gdkdndprivate.h"
+#include "gdkwin32dnd-private.h"
#include "gdkwin32.h"
-/* We emulate the GDK_SELECTION window properties of windows (as used
- * in the X11 backend) by using a hash table from window handles to
- * GdkSelProp structs.
- */
+typedef struct _GdkWin32ClipboardQueueInfo GdkWin32ClipboardQueueInfo;
+
+typedef enum _GdkWin32ClipboardQueueAction GdkWin32ClipboardQueueAction;
+
+enum _GdkWin32ClipboardQueueAction
+{
+ GDK_WIN32_CLIPBOARD_QUEUE_ACTION_CONVERT = 0,
+ GDK_WIN32_CLIPBOARD_QUEUE_ACTION_TARGETS
+};
-typedef struct {
- guchar *data;
- gsize length;
- gint format;
- GdkAtom type;
-} GdkSelProp;
+struct _GdkWin32ClipboardQueueInfo
+{
+ GdkDisplay *display;
+ GdkWindow *requestor;
+ GdkAtom selection;
+ GdkAtom target;
+ guint32 time;
+
+ /* Number of seconds since we started our
+ * attempts to open clipboard.
+ */
+ guint32 idle_time;
-static GHashTable *sel_prop_table = NULL;
+ /* What to do once clipboard is opened */
+ GdkWin32ClipboardQueueAction action;
+};
-static GdkSelProp *dropfiles_prop = NULL;
+/* List of GdkWin32ClipboardQueueInfo slices */
+static GList *clipboard_queue = NULL;
-/* We store the owner of each selection in this table. Obviously, this only
- * is valid intra-app, and in fact it is necessary for the intra-app DND to work.
- */
-static GHashTable *sel_owner_table = NULL;
+#define HIDA_GetPIDLFolder(pida) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[0])
+#define HIDA_GetPIDLItem(pida, i) (LPCITEMIDLIST)(((LPBYTE)pida)+(pida)->aoffset[i+1])
-/* GdkAtoms for well-known image formats */
-static GdkAtom *known_pixbuf_formats;
-static int n_known_pixbuf_formats;
+G_DEFINE_TYPE (GdkWin32Selection, gdk_win32_selection, G_TYPE_OBJECT)
-/* GdkAtoms for well-known text formats */
-static GdkAtom text_plain;
-static GdkAtom text_plain_charset_utf_8;
-static GdkAtom text_plain_charset_CP1252;
+static void
+gdk_win32_selection_class_init (GdkWin32SelectionClass *klass)
+{
+}
void
_gdk_win32_selection_init (void)
{
- GSList *pixbuf_formats;
- GSList *rover;
+ _win32_selection = GDK_WIN32_SELECTION (g_object_new (GDK_TYPE_WIN32_SELECTION, NULL));
+}
+
+static void
+gdk_win32_selection_init (GdkWin32Selection *win32_selection)
+{
+ GArray *atoms;
+ GArray *cfs;
+ GSList *pixbuf_formats;
+ GSList *rover;
+ int i;
+ GArray *comp;
+ GdkSelTargetFormat fmt;
+
+ win32_selection->ignore_destroy_clipboard = FALSE;
+ win32_selection->clipboard_opened_for = INVALID_HANDLE_VALUE;
+
+ win32_selection->dnd_target_state = GDK_WIN32_DND_NONE;
+ win32_selection->dnd_source_state = GDK_WIN32_DND_NONE;
+ win32_selection->dnd_data_object_target = NULL;
+ win32_selection->property_change_format = 0;
+ win32_selection->property_change_data = NULL;
+
+ atoms = g_array_sized_new (FALSE, TRUE, sizeof (GdkAtom), GDK_WIN32_ATOM_INDEX_LAST);
+ g_array_set_size (atoms, GDK_WIN32_ATOM_INDEX_LAST);
+ cfs = g_array_sized_new (FALSE, TRUE, sizeof (UINT), GDK_WIN32_CF_INDEX_LAST);
+ g_array_set_size (cfs, GDK_WIN32_CF_INDEX_LAST);
+
+ win32_selection->known_atoms = atoms;
+ win32_selection->known_clipboard_formats = cfs;
+
+ _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_GDK_SELECTION) = gdk_atom_intern_static_string ("GDK_SELECTION");
+ _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_CLIPBOARD_MANAGER) = gdk_atom_intern_static_string ("CLIPBOARD_MANAGER");
+ _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_WM_TRANSIENT_FOR) = gdk_atom_intern_static_string ("WM_TRANSIENT_FOR");
+ _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_TARGETS) = gdk_atom_intern_static_string ("TARGETS");
+ _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_DELETE) = gdk_atom_intern_static_string ("DELETE");
+ _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_SAVE_TARGETS) = gdk_atom_intern_static_string ("SAVE_TARGETS");
+ _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_UTF8_STRING) = gdk_atom_intern_static_string ("UTF8_STRING");
+ _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_TEXT) = gdk_atom_intern_static_string ("TEXT");
+ _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_COMPOUND_TEXT) = gdk_atom_intern_static_string ("COMPOUND_TEXT");
+ _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_TEXT_URI_LIST) = gdk_atom_intern_static_string ("text/uri-list");
+ _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_TEXT_HTML) = gdk_atom_intern_static_string ("text/html");
+ _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_IMAGE_PNG) = gdk_atom_intern_static_string ("image/png");
+ _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_IMAGE_JPEG) = gdk_atom_intern_static_string ("image/jpeg");
+ _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_IMAGE_BMP) = gdk_atom_intern_static_string ("image/bmp");
+ _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_IMAGE_GIF) = gdk_atom_intern_static_string ("image/gif");
+
+ _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_LOCAL_DND_SELECTION) = gdk_atom_intern_static_string ("LocalDndSelection");
+ _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_DROPFILES_DND) = gdk_atom_intern_static_string ("DROPFILES_DND");
+ _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_OLE2_DND) = gdk_atom_intern_static_string ("OLE2_DND");
+
+ _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_PNG)= gdk_atom_intern_static_string ("PNG");
+ _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_JFIF) = gdk_atom_intern_static_string ("JFIF");
+ _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_GIF) = gdk_atom_intern_static_string ("GIF");
+
+ /* These are a bit unusual. It's here to allow GTK+ applications
+ * to actually support CF_DIB and Shell ID List clipboard formats on their own,
+ * instead of allowing GDK to use them internally for interoperability.
+ */
+ _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_CF_DIB) = gdk_atom_intern_static_string ("CF_DIB");
+ _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_CFSTR_SHELLIDLIST) = gdk_atom_intern_static_string (CFSTR_SHELLIDLIST);
+ _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_CF_UNICODETEXT) = gdk_atom_intern_static_string ("CF_UNICODETEXT");
+ _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_CF_TEXT) = gdk_atom_intern_static_string ("CF_TEXT");
+
+ /* MS Office 2007, at least, offers images in common file formats
+ * using clipboard format names like "PNG" and "JFIF". So we follow
+ * the lead and map the GDK target name "image/png" to the clipboard
+ * format name "PNG" etc.
+ */
+ _gdk_cf_array_index (cfs, GDK_WIN32_CF_INDEX_PNG) = RegisterClipboardFormatA ("PNG");
+ _gdk_cf_array_index (cfs, GDK_WIN32_CF_INDEX_JFIF) = RegisterClipboardFormatA ("JFIF");
+ _gdk_cf_array_index (cfs, GDK_WIN32_CF_INDEX_GIF) = RegisterClipboardFormatA ("GIF");
- sel_prop_table = g_hash_table_new (NULL, NULL);
- sel_owner_table = g_hash_table_new (NULL, NULL);
- _format_atom_table = g_hash_table_new (NULL, NULL);
+ _gdk_cf_array_index (cfs, GDK_WIN32_CF_INDEX_UNIFORMRESOURCELOCATORW) = RegisterClipboardFormatA ("UniformResourceLocatorW");
+ _gdk_cf_array_index (cfs, GDK_WIN32_CF_INDEX_CFSTR_SHELLIDLIST) = RegisterClipboardFormatA (CFSTR_SHELLIDLIST);
+ _gdk_cf_array_index (cfs, GDK_WIN32_CF_INDEX_HTML_FORMAT) = RegisterClipboardFormatA ("HTML Format");
+ _gdk_cf_array_index (cfs, GDK_WIN32_CF_INDEX_TEXT_HTML) = RegisterClipboardFormatA ("text/html");
+
+ _gdk_cf_array_index (cfs, GDK_WIN32_CF_INDEX_IMAGE_PNG) = RegisterClipboardFormatA ("image/png");
+ _gdk_cf_array_index (cfs, GDK_WIN32_CF_INDEX_IMAGE_JPEG) = RegisterClipboardFormatA ("image/jpeg");
+ _gdk_cf_array_index (cfs, GDK_WIN32_CF_INDEX_IMAGE_BMP) = RegisterClipboardFormatA ("image/bmp");
+ _gdk_cf_array_index (cfs, GDK_WIN32_CF_INDEX_IMAGE_GIF) = RegisterClipboardFormatA ("image/gif");
+ _gdk_cf_array_index (cfs, GDK_WIN32_CF_INDEX_TEXT_URI_LIST) = RegisterClipboardFormatA ("text/uri-list");
+ _gdk_cf_array_index (cfs, GDK_WIN32_CF_INDEX_UTF8_STRING) = RegisterClipboardFormatA ("UTF8_STRING");
+
+ win32_selection->sel_prop_table = g_hash_table_new (NULL, NULL);
+ win32_selection->sel_owner_table = g_hash_table_new (NULL, NULL);
pixbuf_formats = gdk_pixbuf_get_formats ();
- n_known_pixbuf_formats = 0;
+ win32_selection->n_known_pixbuf_formats = 0;
for (rover = pixbuf_formats; rover != NULL; rover = rover->next)
{
gchar **mime_types =
gchar **mime_type;
for (mime_type = mime_types; *mime_type != NULL; mime_type++)
- n_known_pixbuf_formats++;
+ win32_selection->n_known_pixbuf_formats++;
}
- known_pixbuf_formats = g_new (GdkAtom, n_known_pixbuf_formats);
+ win32_selection->known_pixbuf_formats = g_new (GdkAtom, win32_selection->n_known_pixbuf_formats);
- n_known_pixbuf_formats = 0;
+ i = 0;
for (rover = pixbuf_formats; rover != NULL; rover = rover->next)
{
gchar **mime_types =
gchar **mime_type;
for (mime_type = mime_types; *mime_type != NULL; mime_type++)
- known_pixbuf_formats[n_known_pixbuf_formats++] = gdk_atom_intern (*mime_type, FALSE);
+ win32_selection->known_pixbuf_formats[i++] = gdk_atom_intern (*mime_type, FALSE);
}
g_slist_free (pixbuf_formats);
- text_plain = gdk_atom_intern ("text/plain", FALSE);
- text_plain_charset_utf_8= gdk_atom_intern ("text/plain;charset=utf-8", FALSE);
- text_plain_charset_CP1252 = gdk_atom_intern ("text/plain;charset=CP1252", FALSE);
+ win32_selection->dnd_selection_targets = g_array_new (FALSE, FALSE, sizeof (GdkSelTargetFormat));
+ win32_selection->clipboard_selection_targets = g_array_new (FALSE, FALSE, sizeof (GdkSelTargetFormat));
+ win32_selection->compatibility_formats = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) g_array_unref);
+
+ /* GTK+ actually has more text formats, but it's unlikely that we'd
+ * get anything other than UTF8_STRING these days.
+ * GTKTEXTBUFFERCONTENTS format can potentially be converted to
+ * W32-compatible rich text format, but that's too complex to address right now.
+ */
+ comp = g_array_sized_new (FALSE, FALSE, sizeof (GdkSelTargetFormat), 3);
+ fmt.target = _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_UTF8_STRING);
+
+ fmt.format = _gdk_cf_array_index (cfs, GDK_WIN32_CF_INDEX_UTF8_STRING);
+ fmt.transmute = FALSE;
+ g_array_append_val (comp, fmt);
+
+ fmt.format = CF_UNICODETEXT;
+ fmt.transmute = TRUE;
+ g_array_append_val (comp, fmt);
+
+ fmt.format = CF_TEXT;
+ g_array_append_val (comp, fmt);
+
+ g_hash_table_replace (win32_selection->compatibility_formats, fmt.target, comp);
+
+
+ comp = g_array_sized_new (FALSE, FALSE, sizeof (GdkSelTargetFormat), 3);
+ fmt.target = _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_IMAGE_PNG);
+
+ fmt.format = _gdk_cf_array_index (cfs, GDK_WIN32_CF_INDEX_IMAGE_PNG);
+ fmt.transmute = FALSE;
+ g_array_append_val (comp, fmt);
+
+ fmt.format = _gdk_cf_array_index (cfs, GDK_WIN32_CF_INDEX_PNG);
+ g_array_append_val (comp, fmt);
+
+ fmt.format = CF_DIB;
+ fmt.transmute = TRUE;
+ g_array_append_val (comp, fmt);
+
+ g_hash_table_replace (win32_selection->compatibility_formats, fmt.target, comp);
+
+
+ comp = g_array_sized_new (FALSE, FALSE, sizeof (GdkSelTargetFormat), 4);
+ fmt.target = _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_IMAGE_JPEG);
+
+ fmt.format = _gdk_cf_array_index (cfs, GDK_WIN32_CF_INDEX_IMAGE_JPEG);
+ fmt.transmute = FALSE;
+ g_array_append_val (comp, fmt);
+
+ fmt.format = _gdk_cf_array_index (cfs, GDK_WIN32_CF_INDEX_JFIF);
+ g_array_append_val (comp, fmt);
+
+ fmt.format = _gdk_cf_array_index (cfs, GDK_WIN32_CF_INDEX_PNG);
+ fmt.transmute = TRUE;
+ g_array_append_val (comp, fmt);
+
+ fmt.format = CF_DIB;
+ g_array_append_val (comp, fmt);
+
+ g_hash_table_replace (win32_selection->compatibility_formats, fmt.target, comp);
+
+
+ comp = g_array_sized_new (FALSE, FALSE, sizeof (GdkSelTargetFormat), 4);
+ fmt.target = _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_IMAGE_GIF);
+
+ fmt.format = _gdk_cf_array_index (cfs, GDK_WIN32_CF_INDEX_IMAGE_GIF);
+ fmt.transmute = FALSE;
+ g_array_append_val (comp, fmt);
+
+ fmt.format = _gdk_cf_array_index (cfs, GDK_WIN32_CF_INDEX_GIF);
+ g_array_append_val (comp, fmt);
+
+ fmt.format = _gdk_cf_array_index (cfs, GDK_WIN32_CF_INDEX_PNG);
+ fmt.transmute = TRUE;
+ g_array_append_val (comp, fmt);
+
+ fmt.format = CF_DIB;
+ g_array_append_val (comp, fmt);
+
+ g_hash_table_replace (win32_selection->compatibility_formats, fmt.target, comp);
+
+
+ comp = g_array_sized_new (FALSE, FALSE, sizeof (GdkSelTargetFormat), 2);
+ fmt.target = _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_IMAGE_BMP);
+
+ fmt.format = _gdk_cf_array_index (cfs, GDK_WIN32_CF_INDEX_IMAGE_BMP);
+ fmt.transmute = FALSE;
+ g_array_append_val (comp, fmt);
+
+ fmt.format = CF_DIB;
+ fmt.transmute = TRUE;
+ g_array_append_val (comp, fmt);
+
+ g_hash_table_replace (win32_selection->compatibility_formats, fmt.target, comp);
+
+
+/* Not implemented, but definitely possible
+ comp = g_array_sized_new (FALSE, FALSE, 2);
+ fmt.target = _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_TEXT_URI_LIST);
+
+ fmt.format = _gdk_cf_array_index (cfs, GDK_WIN32_CF_INDEX_TEXT_URI_LIST);
+ fmt.transmute = FALSE;
+ g_array_append_val (comp, fmt);
+
+ fmt.format = _gdk_cf_array_index (cfs, GDK_WIN32_CF_INDEX_CFSTR_SHELLIDLIST);
+ fmt.transmute = TRUE;
+ g_array_append_val (comp, fmt);
+
+ g_hash_table_replace (win32_selection->compatibility_formats, fmt.target, comp);
+*/
+
+ win32_selection->compatibility_targets = g_hash_table_new_full (NULL, NULL, NULL, (GDestroyNotify) g_array_unref);
+
+ comp = g_array_sized_new (FALSE, FALSE, sizeof (GdkSelTargetFormat), 2);
+ fmt.format = CF_TEXT;
+ fmt.transmute = FALSE;
+
+ fmt.target = _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_CF_TEXT);
+ g_array_append_val (comp, fmt);
+
+ fmt.target = _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_UTF8_STRING);
+ fmt.transmute = TRUE;
+ g_array_append_val (comp, fmt);
+
+ g_hash_table_replace (win32_selection->compatibility_targets, GINT_TO_POINTER (CF_TEXT), comp);
+
- g_hash_table_replace (_format_atom_table,
- GINT_TO_POINTER (_cf_png),
- _image_png);
+ comp = g_array_sized_new (FALSE, FALSE, sizeof (GdkSelTargetFormat), 2);
+ fmt.format = CF_UNICODETEXT;
+ fmt.transmute = FALSE;
- g_hash_table_replace (_format_atom_table,
- GINT_TO_POINTER (CF_DIB),
- _image_bmp);
+ fmt.target = _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_CF_UNICODETEXT);
+ g_array_append_val (comp, fmt);
+
+ fmt.target = _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_UTF8_STRING);
+ fmt.transmute = TRUE;
+ g_array_append_val (comp, fmt);
+
+ g_hash_table_replace (win32_selection->compatibility_targets, GINT_TO_POINTER (CF_UNICODETEXT), comp);
+
+
+ comp = g_array_sized_new (FALSE, FALSE, sizeof (GdkSelTargetFormat), 3);
+ fmt.format = _gdk_cf_array_index (cfs, GDK_WIN32_CF_INDEX_PNG);
+ fmt.transmute = FALSE;
+
+ fmt.target = _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_PNG);
+ g_array_append_val (comp, fmt);
+
+ fmt.target = _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_IMAGE_PNG);
+ g_array_append_val (comp, fmt);
+
+ fmt.target = _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_IMAGE_BMP);
+ fmt.transmute = TRUE;
+ g_array_append_val (comp, fmt);
+
+ g_hash_table_replace (win32_selection->compatibility_targets, GINT_TO_POINTER (_gdk_cf_array_index (cfs, GDK_WIN32_CF_INDEX_PNG)), comp);
+
+
+ comp = g_array_sized_new (FALSE, FALSE, sizeof (GdkSelTargetFormat), 4);
+ fmt.format = _gdk_cf_array_index (cfs, GDK_WIN32_CF_INDEX_JFIF);
+ fmt.transmute = FALSE;
+
+ fmt.target = _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_JFIF);
+ g_array_append_val (comp, fmt);
+
+ fmt.target = _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_IMAGE_JPEG);
+ g_array_append_val (comp, fmt);
+
+ fmt.target = _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_IMAGE_PNG);
+ fmt.transmute = TRUE;
+ g_array_append_val (comp, fmt);
+
+ fmt.target = _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_IMAGE_BMP);
+ g_array_append_val (comp, fmt);
+
+ g_hash_table_replace (win32_selection->compatibility_targets, GINT_TO_POINTER (_gdk_cf_array_index (cfs, GDK_WIN32_CF_INDEX_JFIF)), comp);
+
+
+ comp = g_array_sized_new (FALSE, FALSE, sizeof (GdkSelTargetFormat), 4);
+ fmt.format = _gdk_cf_array_index (cfs, GDK_WIN32_CF_INDEX_GIF);
+ fmt.transmute = FALSE;
+
+ fmt.target = _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_GIF);
+ g_array_append_val (comp, fmt);
+
+ fmt.target = _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_IMAGE_GIF);
+ g_array_append_val (comp, fmt);
+
+ fmt.target = _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_IMAGE_PNG);
+ fmt.transmute = TRUE;
+ g_array_append_val (comp, fmt);
+
+ fmt.target = _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_IMAGE_BMP);
+ g_array_append_val (comp, fmt);
+
+ g_hash_table_replace (win32_selection->compatibility_targets, GINT_TO_POINTER (_gdk_cf_array_index (cfs, GDK_WIN32_CF_INDEX_GIF)), comp);
+
+
+ comp = g_array_sized_new (FALSE, FALSE, sizeof (GdkSelTargetFormat), 3);
+ fmt.format = CF_DIB;
+ fmt.transmute = FALSE;
+
+ fmt.target = _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_CF_DIB);
+ g_array_append_val (comp, fmt);
+
+ fmt.target = _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_IMAGE_BMP);
+ fmt.transmute = TRUE;
+ g_array_append_val (comp, fmt);
+
+ g_hash_table_replace (win32_selection->compatibility_targets, GINT_TO_POINTER (CF_DIB), comp);
+
+
+ comp = g_array_sized_new (FALSE, FALSE, sizeof (GdkSelTargetFormat), 3);
+ fmt.format = _gdk_cf_array_index (cfs, GDK_WIN32_CF_INDEX_CFSTR_SHELLIDLIST);
+ fmt.transmute = FALSE;
+
+ fmt.target = _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_CFSTR_SHELLIDLIST);
+ g_array_append_val (comp, fmt);
+
+ fmt.target = _gdk_atom_array_index (atoms, GDK_WIN32_ATOM_INDEX_TEXT_URI_LIST);
+ fmt.transmute = TRUE;
+ g_array_append_val (comp, fmt);
+
+ g_hash_table_replace (win32_selection->compatibility_targets, GINT_TO_POINTER (_gdk_cf_array_index (cfs, GDK_WIN32_CF_INDEX_CFSTR_SHELLIDLIST)), comp);
}
/* The specifications for COMPOUND_TEXT and STRING specify that C0 and
gint length)
{
GdkSelProp *prop;
+ GdkWin32Selection *win32_sel = _gdk_win32_selection_get ();
g_return_if_fail (type != GDK_TARGET_STRING);
- prop = g_hash_table_lookup (sel_prop_table, GDK_WINDOW_HWND (owner));
+ prop = g_hash_table_lookup (win32_sel->sel_prop_table, GDK_WINDOW_HWND (owner));
if (prop != NULL)
{
g_free (prop->data);
g_free (prop);
- g_hash_table_remove (sel_prop_table, GDK_WINDOW_HWND (owner));
+ g_hash_table_remove (win32_sel->sel_prop_table, GDK_WINDOW_HWND (owner));
}
prop = g_new (GdkSelProp, 1);
prop->data = data;
prop->length = length;
- prop->format = format;
- prop->type = type;
+ prop->bitness = format;
+ prop->target = type;
- g_hash_table_insert (sel_prop_table, GDK_WINDOW_HWND (owner), prop);
+ g_hash_table_insert (win32_sel->sel_prop_table, GDK_WINDOW_HWND (owner), prop);
}
void
_gdk_dropfiles_store (gchar *data)
{
+ GdkWin32Selection *win32_sel = _gdk_win32_selection_get ();
+
if (data != NULL)
{
- g_assert (dropfiles_prop == NULL);
+ g_assert (win32_sel->dropfiles_prop == NULL);
- dropfiles_prop = g_new (GdkSelProp, 1);
- dropfiles_prop->data = (guchar *) data;
- dropfiles_prop->length = strlen (data) + 1;
- dropfiles_prop->format = 8;
- dropfiles_prop->type = _text_uri_list;
+ win32_sel->dropfiles_prop = g_new (GdkSelProp, 1);
+ win32_sel->dropfiles_prop->data = (guchar *) data;
+ win32_sel->dropfiles_prop->length = strlen (data) + 1;
+ win32_sel->dropfiles_prop->bitness = 8;
+ win32_sel->dropfiles_prop->target = _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_TEXT_URI_LIST);
}
else
{
- if (dropfiles_prop != NULL)
+ if (win32_sel->dropfiles_prop != NULL)
{
- g_free (dropfiles_prop->data);
- g_free (dropfiles_prop);
+ g_free (win32_sel->dropfiles_prop->data);
+ g_free (win32_sel->dropfiles_prop);
}
- dropfiles_prop = NULL;
+ win32_sel->dropfiles_prop = NULL;
}
}
-static gchar *
-get_mapped_gdk_atom_name (GdkAtom gdk_target)
+static void
+generate_selection_notify (GdkWindow *requestor,
+ GdkAtom selection,
+ GdkAtom target,
+ GdkAtom property,
+ guint32 time)
+{
+ GdkEvent tmp_event;
+
+ memset (&tmp_event, 0, sizeof (tmp_event));
+ tmp_event.selection.type = GDK_SELECTION_NOTIFY;
+ tmp_event.selection.window = requestor;
+ tmp_event.selection.send_event = FALSE;
+ tmp_event.selection.selection = selection;
+ tmp_event.selection.target = target;
+ tmp_event.selection.property = property;
+ tmp_event.selection.requestor = 0;
+ tmp_event.selection.time = time;
+
+ gdk_event_put (&tmp_event);
+}
+
+void
+_gdk_win32_clear_clipboard_queue ()
{
- if (gdk_target == _image_png)
- return g_strdup ("PNG");
+ GList *tmp_list, *next;
+ GdkWin32ClipboardQueueInfo *info;
+ GdkWin32Selection *win32_sel = _gdk_win32_selection_get ();
- if (gdk_target == _image_jpeg)
- return g_strdup ("JFIF");
+ GDK_NOTE (DND, g_print ("Clear clipboard queue\n"));
- if (gdk_target == _image_gif)
- return g_strdup ("GIF");
+ for (tmp_list = clipboard_queue; tmp_list; tmp_list = next)
+ {
+ info = (GdkWin32ClipboardQueueInfo *) tmp_list->data;
+ next = g_list_next (tmp_list);
+ clipboard_queue = g_list_remove_link (clipboard_queue, tmp_list);
+ g_list_free (tmp_list);
+ switch (info->action)
+ {
+ case GDK_WIN32_CLIPBOARD_QUEUE_ACTION_TARGETS:
+ break;
+ case GDK_WIN32_CLIPBOARD_QUEUE_ACTION_CONVERT:
+ generate_selection_notify (info->requestor, info->selection, info->target, GDK_NONE, info->time);
+ break;
+ }
+ g_clear_object (&info->requestor);
+ g_slice_free (GdkWin32ClipboardQueueInfo, info);
+ }
- return gdk_atom_name (gdk_target);
+ win32_sel->targets_request_pending = FALSE;
}
-gboolean
-_gdk_win32_display_set_selection_owner (GdkDisplay *display,
- GdkWindow *owner,
- GdkAtom selection,
- guint32 time,
- gboolean send_event)
+/* Send ourselves a selection request message with
+ * the TARGETS target, we will do multiple SetClipboarData(...,NULL)
+ * calls in response to announce the formats we support.
+ */
+static void
+send_targets_request (guint time)
{
- HWND hwnd;
+ GdkWindow *owner;
GdkEvent tmp_event;
+ GdkWin32Selection *win32_sel = _gdk_win32_selection_get ();
- g_return_val_if_fail (selection != GDK_NONE, FALSE);
+ if (win32_sel->targets_request_pending)
+ return;
- GDK_NOTE (DND, {
- gchar *sel_name = gdk_atom_name (selection);
+ owner = _gdk_win32_display_get_selection_owner (gdk_display_get_default (),
+ GDK_SELECTION_CLIPBOARD);
- g_print ("gdk_selection_owner_set_for_display: %p %s\n",
- (owner ? GDK_WINDOW_HWND (owner) : NULL),
- sel_name);
- g_free (sel_name);
- });
+ if (owner == NULL)
+ return;
- if (selection != GDK_SELECTION_CLIPBOARD)
+ if (win32_sel->clipboard_opened_for == INVALID_HANDLE_VALUE)
{
- if (owner != NULL)
- g_hash_table_insert (sel_owner_table, selection, GDK_WINDOW_HWND (owner));
- else
- g_hash_table_remove (sel_owner_table, selection);
- return TRUE;
+ if (OpenClipboard (GDK_WINDOW_HWND (owner)))
+ {
+ win32_sel->clipboard_opened_for = GDK_WINDOW_HWND (owner);
+ GDK_NOTE (DND, g_print ("Opened clipboard for 0x%p @ %s:%d\n", win32_sel->clipboard_opened_for, __FILE__, __LINE__));
+ }
}
- /* Rest of this function handles the CLIPBOARD selection */
- if (owner != NULL)
+ GDK_NOTE (DND, g_print ("... sending GDK_SELECTION_REQUEST to ourselves\n"));
+ memset (&tmp_event, 0, sizeof (tmp_event));
+ tmp_event.selection.type = GDK_SELECTION_REQUEST;
+ tmp_event.selection.window = owner;
+ tmp_event.selection.send_event = FALSE;
+ tmp_event.selection.selection = GDK_SELECTION_CLIPBOARD;
+ tmp_event.selection.target = _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_TARGETS);
+ tmp_event.selection.property = _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_GDK_SELECTION);
+ tmp_event.selection.requestor = owner;
+ tmp_event.selection.time = time;
+
+ gdk_event_put (&tmp_event);
+ win32_sel->targets_request_pending = TRUE;
+}
+
+#define CLIPBOARD_IDLE_ABORT_TIME 30
+
+static const gchar *
+predefined_name (UINT fmt)
+{
+#define CASE(fmt) case fmt: return #fmt
+ switch (fmt)
{
- if (GDK_WINDOW_DESTROYED (owner))
- return FALSE;
+ CASE (CF_TEXT);
+ CASE (CF_BITMAP);
+ CASE (CF_METAFILEPICT);
+ CASE (CF_SYLK);
+ CASE (CF_DIF);
+ CASE (CF_TIFF);
+ CASE (CF_OEMTEXT);
+ CASE (CF_DIB);
+ CASE (CF_PALETTE);
+ CASE (CF_PENDATA);
+ CASE (CF_RIFF);
+ CASE (CF_WAVE);
+ CASE (CF_UNICODETEXT);
+ CASE (CF_ENHMETAFILE);
+ CASE (CF_HDROP);
+ CASE (CF_LOCALE);
+ CASE (CF_DIBV5);
+ CASE (CF_MAX);
+
+ CASE (CF_OWNERDISPLAY);
+ CASE (CF_DSPTEXT);
+ CASE (CF_DSPBITMAP);
+ CASE (CF_DSPMETAFILEPICT);
+ CASE (CF_DSPENHMETAFILE);
+#undef CASE
+ default:
+ return NULL;
+ }
+}
- hwnd = GDK_WINDOW_HWND (owner);
+gchar *
+_gdk_win32_get_clipboard_format_name (UINT fmt,
+ gboolean *is_predefined)
+{
+ gint registered_name_w_len = 1024;
+ wchar_t *registered_name_w = g_malloc (registered_name_w_len);
+ gchar *registered_name = NULL;
+ int gcfn_result;
+ const gchar *predef = predefined_name (fmt);
+
+ /* FIXME: cache the result in a hash table */
+
+ do
+ {
+ gcfn_result = GetClipboardFormatNameW (fmt, registered_name_w, registered_name_w_len);
+
+ if (gcfn_result > 0 && gcfn_result < registered_name_w_len)
+ {
+ registered_name = g_utf16_to_utf8 (registered_name_w, -1, NULL, NULL, NULL);
+ g_clear_pointer (®istered_name_w, g_free);
+ if (!registered_name)
+ gcfn_result = 0;
+ else
+ *is_predefined = FALSE;
+ break;
+ }
+
+ /* If GetClipboardFormatNameW() used up all the space, it means that
+ * we probably need a bigger buffer, but cap this at 1 kilobyte.
+ */
+ if (gcfn_result == 0 || registered_name_w_len > 1024 * 1024)
+ {
+ gcfn_result = 0;
+ g_clear_pointer (®istered_name_w, g_free);
+ break;
+ }
+
+ registered_name_w_len *= 2;
+ registered_name_w = g_realloc (registered_name_w, registered_name_w_len);
+ gcfn_result = registered_name_w_len;
+ } while (gcfn_result == registered_name_w_len);
+
+ if (registered_name == NULL &&
+ predef != NULL)
+ {
+ registered_name = g_strdup (predef);
+ *is_predefined = TRUE;
}
- else
- hwnd = NULL;
- if (!API_CALL (OpenClipboard, (hwnd)))
- return FALSE;
+ return registered_name;
+}
+
+static GArray *
+get_compatibility_formats_for_target (GdkAtom target)
+{
+ GArray *result = NULL;
+ gint i;
+ GdkWin32Selection *win32_sel = _gdk_win32_selection_get ();
+
+ result = g_hash_table_lookup (win32_sel->compatibility_formats, target);
+
+ if (result != NULL)
+ return result;
+
+ for (i = 0; i < win32_sel->n_known_pixbuf_formats; i++)
+ {
+ if (target != win32_sel->known_pixbuf_formats[i])
+ continue;
+
+ /* Any format known to gdk-pixbuf can be presented as PNG or BMP */
+ result = g_hash_table_lookup (win32_sel->compatibility_formats,
+ _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_IMAGE_PNG));
+ break;
+ }
+
+ return result;
+}
+
+static GArray *
+_gdk_win32_selection_get_compatibility_targets_for_format (UINT format)
+{
+ GArray *result = NULL;
+ GdkWin32Selection *win32_sel = _gdk_win32_selection_get ();
+
+ result = g_hash_table_lookup (win32_sel->compatibility_targets, GINT_TO_POINTER (format));
+
+ if (result != NULL)
+ return result;
+
+ /* TODO: reverse gdk-pixbuf conversion? We have to somehow
+ * match gdk-pixbuf format names to the corresponding clipboard
+ * format names. The former are known only at runtime,
+ * the latter are presently unknown...
+ * Maybe try to get the data and then just feed it to gdk-pixbuf,
+ * see if it knows what it is?
+ */
+
+ return result;
+}
+
+void
+_gdk_win32_add_format_to_targets (UINT format,
+ GArray *array,
+ GList **list)
+{
+ gboolean predef;
+ gchar *format_name = _gdk_win32_get_clipboard_format_name (format, &predef);
+ GdkAtom target_atom;
+ GdkSelTargetFormat target_selformat;
+ GArray *target_selformats;
+ gint i,j;
+
+ if (format_name != NULL)
+ {
+ target_atom = gdk_atom_intern (format_name, FALSE);
+ GDK_NOTE (DND, g_print ("Maybe add as-is format %s (0x%p)\n", format_name, target_atom));
+ g_free (format_name);
+ if (array && target_atom != 0)
+ {
+ for (j = 0; j < array->len; j++)
+ if (g_array_index (array, GdkSelTargetFormat, j).target == target_atom)
+ break;
+ if (j == array->len)
+ {
+ target_selformat.format = format;
+ target_selformat.target = target_atom;
+ target_selformat.transmute = FALSE;
+ g_array_append_val (array, target_selformat);
+ }
+ }
+ if (list && target_atom != 0 && g_list_find (*list, target_atom) == NULL)
+ *list = g_list_prepend (*list, target_atom);
+ }
+
+ target_selformats = _gdk_win32_selection_get_compatibility_targets_for_format (format);
+
+ if (array && target_selformats != NULL)
+ for (i = 0; i < target_selformats->len; i++)
+ {
+ target_selformat = g_array_index (target_selformats, GdkSelTargetFormat, i);
+
+ for (j = 0; j < array->len; j++)
+ if (g_array_index (array, GdkSelTargetFormat, j).target == target_selformat.target &&
+ g_array_index (array, GdkSelTargetFormat, j).format == target_selformat.format)
+ break;
+
+ if (j == array->len)
+ g_array_append_val (array, target_selformat);
+ }
+
+ if (list && target_selformats != NULL)
+ for (i = 0; i < target_selformats->len; i++)
+ {
+ target_selformat = g_array_index (target_selformats, GdkSelTargetFormat, i);
+
+ if (g_list_find (*list, target_selformat.target) == NULL)
+ *list = g_list_prepend (*list, target_selformat.target);
+ }
+}
+
+static void
+transmute_cf_unicodetext_to_utf8_string (const guchar *data,
+ gint length,
+ guchar **set_data,
+ gint *set_data_length,
+ GDestroyNotify *set_data_destroy)
+{
+ wchar_t *ptr, *p, *q;
+ gchar *result;
+ glong wclen, u8_len;
+
+ /* Strip out \r */
+ ptr = (wchar_t *) data;
+ p = ptr;
+ q = ptr;
+ wclen = 0;
+
+ while (p < ptr + length / 2)
+ {
+ if (*p != L'\r')
+ {
+ *q++ = *p;
+ wclen++;
+ }
+ p++;
+ }
+
+ result = g_utf16_to_utf8 (ptr, wclen, NULL, &u8_len, NULL);
+
+ if (result)
+ {
+ *set_data = (guchar *) result;
+
+ if (set_data_length)
+ *set_data_length = u8_len + 1;
+ if (set_data_destroy)
+ *set_data_destroy = (GDestroyNotify) g_free;
+ }
+}
+
+static void
+transmute_utf8_string_to_cf_unicodetext (const guchar *data,
+ gint length,
+ guchar **set_data,
+ gint *set_data_length,
+ GDestroyNotify *set_data_destroy)
+{
+ glong wclen;
+ GError *err = NULL;
+ glong size;
+ gint i;
+ wchar_t *wcptr, *p;
+
+ wcptr = g_utf8_to_utf16 ((char *) data, length, NULL, &wclen, &err);
+ if (err != NULL)
+ {
+ g_warning ("Failed to convert utf8: %s", err->message);
+ g_clear_error (&err);
+ return;
+ }
+
+ wclen++; /* Terminating 0 */
+ size = wclen * 2;
+ for (i = 0; i < wclen; i++)
+ if (wcptr[i] == L'\n' && (i == 0 || wcptr[i - 1] != L'\r'))
+ size += 2;
+
+ *set_data = g_malloc0 (size);
+ if (set_data_length)
+ *set_data_length = size;
+ if (set_data_destroy)
+ *set_data_destroy = (GDestroyNotify) g_free;
+
+ p = (wchar_t *) *set_data;
+
+ for (i = 0; i < wclen; i++)
+ {
+ if (wcptr[i] == L'\n' && (i == 0 || wcptr[i - 1] != L'\r'))
+ *p++ = L'\r';
+ *p++ = wcptr[i];
+ }
+
+ g_free (wcptr);
+}
+
+static int
+wchar_to_str (const wchar_t *wstr,
+ char **retstr,
+ UINT cp)
+{
+ char *str;
+ int len;
+ int lenc;
+
+ len = WideCharToMultiByte (cp, 0, wstr, -1, NULL, 0, NULL, NULL);
+
+ if (len <= 0)
+ return -1;
+
+ str = g_malloc (sizeof (char) * len);
+
+ lenc = WideCharToMultiByte (cp, 0, wstr, -1, str, len, NULL, NULL);
+
+ if (lenc != len)
+ {
+ g_free (str);
+
+ return -3;
+ }
+
+ *retstr = str;
+
+ return 0;
+}
+
+static void
+transmute_utf8_string_to_cf_text (const guchar *data,
+ gint length,
+ guchar **set_data,
+ gint *set_data_length,
+ GDestroyNotify *set_data_destroy)
+{
+ glong rlen;
+ GError *err = NULL;
+ glong size;
+ gint i;
+ char *strptr, *p;
+ wchar_t *wcptr;
+
+ wcptr = g_utf8_to_utf16 ((char *) data, length, NULL, NULL, &err);
+ if (err != NULL)
+ {
+ g_warning ("Failed to convert utf8: %s", err->message);
+ g_clear_error (&err);
+ return;
+ }
+
+ if (wchar_to_str (wcptr, &strptr, CP_ACP) != 0)
+ {
+ g_warning ("Failed to convert utf-16 %S to ACP", wcptr);
+ g_free (wcptr);
+ return;
+ }
+
+ rlen = strlen (strptr);
+ g_free (wcptr);
+
+ rlen++; /* Terminating 0 */
+ size = rlen * sizeof (char);
+ for (i = 0; i < rlen; i++)
+ if (strptr[i] == '\n' && (i == 0 || strptr[i - 1] != '\r'))
+ size += sizeof (char);
+
+ *set_data = g_malloc0 (size);
+ if (set_data_length)
+ *set_data_length = size;
+ if (set_data_destroy)
+ *set_data_destroy = (GDestroyNotify) g_free;
+
+ p = (char *) *set_data;
+
+ for (i = 0; i < rlen; i++)
+ {
+ if (strptr[i] == '\n' && (i == 0 || strptr[i - 1] != '\r'))
+ *p++ = '\r';
+ *p++ = strptr[i];
+ }
+
+ g_free (strptr);
+}
+
+static int
+str_to_wchar (const char *str,
+ wchar_t **wretstr,
+ UINT cp)
+{
+ wchar_t *wstr;
+ int len;
+ int lenc;
+
+ len = MultiByteToWideChar (cp, 0, str, -1, NULL, 0);
+
+ if (len <= 0)
+ return -1;
+
+ wstr = g_malloc (sizeof (wchar_t) * len);
+
+ lenc = MultiByteToWideChar (cp, 0, str, -1, wstr, len);
+
+ if (lenc != len)
+ {
+ g_free (wstr);
+
+ return -3;
+ }
+
+ *wretstr = wstr;
+
+ return 0;
+}
+
+static void
+transmute_cf_text_to_utf8_string (const guchar *data,
+ gint length,
+ guchar **set_data,
+ gint *set_data_length,
+ GDestroyNotify *set_data_destroy)
+{
+ char *ptr, *p, *q;
+ gchar *result;
+ glong wclen, u8_len;
+ wchar_t *wstr;
+
+ /* Strip out \r */
+ ptr = (gchar *) data;
+ p = ptr;
+ q = ptr;
+ wclen = 0;
+
+ while (p < ptr + length / 2)
+ {
+ if (*p != '\r')
+ {
+ *q++ = *p;
+ wclen++;
+ }
+ p++;
+ }
+
+ if (str_to_wchar (ptr, &wstr, CP_ACP) < 0)
+ return;
+
+ result = g_utf16_to_utf8 (wstr, -1, NULL, &u8_len, NULL);
+
+ if (result)
+ {
+ *set_data = (guchar *) result;
+
+ if (set_data_length)
+ *set_data_length = u8_len + 1;
+ if (set_data_destroy)
+ *set_data_destroy = (GDestroyNotify) g_free;
+ }
+
+ g_free (wstr);
+}
+
+static void
+transmute_cf_dib_to_image_bmp (const guchar *data,
+ gint length,
+ guchar **set_data,
+ gint *set_data_length,
+ GDestroyNotify *set_data_destroy)
+{
+ /* Need to add a BMP file header so gdk-pixbuf can load
+ * it.
+ *
+ * If the data is from Mozilla Firefox or IE7, and
+ * starts with an "old fashioned" BITMAPINFOHEADER,
+ * i.e. with biSize==40, and biCompression == BI_RGB and
+ * biBitCount==32, we assume that the "extra" byte in
+ * each pixel in fact is alpha.
+ *
+ * The gdk-pixbuf bmp loader doesn't trust 32-bit BI_RGB
+ * bitmaps to in fact have alpha, so we have to convince
+ * it by changing the bitmap header to a version 5
+ * BI_BITFIELDS one with explicit alpha mask indicated.
+ *
+ * The RGB bytes that are in bitmaps on the clipboard
+ * originating from Firefox or IE7 seem to be
+ * premultiplied with alpha. The gdk-pixbuf bmp loader
+ * of course doesn't expect that, so we have to undo the
+ * premultiplication before feeding the bitmap to the
+ * bmp loader.
+ *
+ * Note that for some reason the bmp loader used to want
+ * the alpha bytes in its input to actually be
+ * 255-alpha, but here we assume that this has been
+ * fixed before this is committed.
+ */
+ BITMAPINFOHEADER *bi = (BITMAPINFOHEADER *) data;
+ BITMAPFILEHEADER *bf;
+ gpointer result;
+ gint data_length = length;
+ gint new_length;
+ gboolean make_dibv5 = FALSE;
+ BITMAPV5HEADER *bV5;
+ guchar *p;
+ guint i;
+
+ if (bi->biSize == sizeof (BITMAPINFOHEADER) &&
+ bi->biPlanes == 1 &&
+ bi->biBitCount == 32 &&
+ bi->biCompression == BI_RGB &&
+#if 0
+ /* Maybe check explicitly for Mozilla or IE7?
+ *
+ * If the clipboard format
+ * application/x-moz-nativeimage is present, that is
+ * a reliable indicator that the data is offered by
+ * Mozilla one would think. For IE7,
+ * UniformResourceLocatorW is presumably not that
+ * uniqie, so probably need to do some
+ * GetClipboardOwner(), GetWindowThreadProcessId(),
+ * OpenProcess(), GetModuleFileNameEx() dance to
+ * check?
+ */
+ (IsClipboardFormatAvailable
+ (RegisterClipboardFormatA ("application/x-moz-nativeimage")) ||
+ IsClipboardFormatAvailable
+ (RegisterClipboardFormatA ("UniformResourceLocatorW"))) &&
+#endif
+ TRUE)
+ {
+ /* We turn the BITMAPINFOHEADER into a
+ * BITMAPV5HEADER before feeding it to gdk-pixbuf.
+ */
+ new_length = (data_length +
+ sizeof (BITMAPFILEHEADER) +
+ (sizeof (BITMAPV5HEADER) - sizeof (BITMAPINFOHEADER)));
+ make_dibv5 = TRUE;
+ }
+ else
+ {
+ new_length = data_length + sizeof (BITMAPFILEHEADER);
+ }
+
+ result = g_try_malloc (new_length);
+
+ if (result == NULL)
+ return;
+
+ bf = (BITMAPFILEHEADER *) result;
+ bf->bfType = 0x4d42; /* "BM" */
+ bf->bfSize = new_length;
+ bf->bfReserved1 = 0;
+ bf->bfReserved2 = 0;
+
+ *set_data = result;
+
+ if (set_data_length)
+ *set_data_length = new_length;
+ if (set_data_destroy)
+ *set_data_destroy = g_free;
+
+ if (!make_dibv5)
+ {
+ bf->bfOffBits = (sizeof (BITMAPFILEHEADER) +
+ bi->biSize +
+ bi->biClrUsed * sizeof (RGBQUAD));
+
+ if (bi->biCompression == BI_BITFIELDS && bi->biBitCount >= 16)
+ {
+ /* Screenshots taken with PrintScreen or
+ * Alt + PrintScreen are found on the clipboard in
+ * this format. In this case the BITMAPINFOHEADER is
+ * followed by three DWORD specifying the masks of the
+ * red green and blue components, so adjust the offset
+ * accordingly. */
+ bf->bfOffBits += (3 * sizeof (DWORD));
+ }
+
+ memcpy ((char *) result + sizeof (BITMAPFILEHEADER),
+ bi,
+ data_length);
+
+ return;
+ }
+
+ bV5 = (BITMAPV5HEADER *) ((char *) result + sizeof (BITMAPFILEHEADER));
+
+ bV5->bV5Size = sizeof (BITMAPV5HEADER);
+ bV5->bV5Width = bi->biWidth;
+ bV5->bV5Height = bi->biHeight;
+ bV5->bV5Planes = 1;
+ bV5->bV5BitCount = 32;
+ bV5->bV5Compression = BI_BITFIELDS;
+ bV5->bV5SizeImage = 4 * bV5->bV5Width * ABS (bV5->bV5Height);
+ bV5->bV5XPelsPerMeter = bi->biXPelsPerMeter;
+ bV5->bV5YPelsPerMeter = bi->biYPelsPerMeter;
+ bV5->bV5ClrUsed = 0;
+ bV5->bV5ClrImportant = 0;
+ /* Now the added mask fields */
+ bV5->bV5RedMask = 0x00ff0000;
+ bV5->bV5GreenMask = 0x0000ff00;
+ bV5->bV5BlueMask = 0x000000ff;
+ bV5->bV5AlphaMask = 0xff000000;
+ ((char *) &bV5->bV5CSType)[3] = 's';
+ ((char *) &bV5->bV5CSType)[2] = 'R';
+ ((char *) &bV5->bV5CSType)[1] = 'G';
+ ((char *) &bV5->bV5CSType)[0] = 'B';
+ /* Ignore colorspace and profile fields */
+ bV5->bV5Intent = LCS_GM_GRAPHICS;
+ bV5->bV5Reserved = 0;
+
+ bf->bfOffBits = (sizeof (BITMAPFILEHEADER) +
+ bV5->bV5Size);
+
+ p = ((guchar *) result) + sizeof (BITMAPFILEHEADER) + sizeof (BITMAPV5HEADER);
+ memcpy (p, ((char *) bi) + bi->biSize,
+ data_length - sizeof (BITMAPINFOHEADER));
+
+ for (i = 0; i < bV5->bV5SizeImage/4; i++)
+ {
+ if (p[3] != 0)
+ {
+ gdouble inverse_alpha = 255./p[3];
+
+ p[0] = p[0] * inverse_alpha + 0.5;
+ p[1] = p[1] * inverse_alpha + 0.5;
+ p[2] = p[2] * inverse_alpha + 0.5;
+ }
+
+ p += 4;
+ }
+}
+
+static void
+transmute_cf_shell_id_list_to_text_uri_list (const guchar *data,
+ gint length,
+ guchar **set_data,
+ gint *set_data_length,
+ GDestroyNotify *set_data_destroy)
+{
+ guint i;
+ CIDA *cida = (CIDA *) data;
+ guint number_of_ids = cida->cidl;
+ GString *result = g_string_new (NULL);
+ PCIDLIST_ABSOLUTE folder_id = HIDA_GetPIDLFolder (cida);
+ wchar_t path_w[MAX_PATH + 1];
+
+ for (i = 0; i < number_of_ids; i++)
+ {
+ PCUIDLIST_RELATIVE file_id = HIDA_GetPIDLItem (cida, i);
+ PIDLIST_ABSOLUTE file_id_full = ILCombine (folder_id, file_id);
+
+ if (SHGetPathFromIDListW (file_id_full, path_w))
+ {
+ gchar *filename = g_utf16_to_utf8 (path_w, -1, NULL, NULL, NULL);
+
+ if (filename)
+ {
+ gchar *uri = g_filename_to_uri (filename, NULL, NULL);
+
+ if (uri)
+ {
+ g_string_append (result, uri);
+ g_string_append (result, "\r\n");
+ g_free (uri);
+ }
+
+ g_free (filename);
+ }
+ }
+
+ ILFree (file_id_full);
+ }
+
+ *set_data = (guchar *) result->str;
+ if (set_data_length)
+ *set_data_length = result->len;
+ if (set_data_destroy)
+ *set_data_destroy = g_free;
+
+ g_string_free (result, FALSE);
+}
+
+void
+transmute_image_bmp_to_cf_dib (const guchar *data,
+ gint length,
+ guchar **set_data,
+ gint *set_data_length,
+ GDestroyNotify *set_data_destroy)
+{
+ gint size;
+ guchar *ptr;
+
+ g_return_if_fail (length >= sizeof (BITMAPFILEHEADER));
+
+ /* No conversion is needed, just strip the BITMAPFILEHEADER */
+ size = length - sizeof (BITMAPFILEHEADER);
+ ptr = g_malloc (size);
+
+ memcpy (ptr, data + sizeof (BITMAPFILEHEADER), size);
+
+ *set_data = ptr;
+
+ if (set_data_length)
+ *set_data_length = size;
+ if (set_data_destroy)
+ *set_data_destroy = g_free;
+}
+
+static void
+transmute_selection_format (UINT from_format,
+ GdkAtom to_target,
+ const guchar *data,
+ gint length,
+ guchar **set_data,
+ gint *set_data_length)
+{
+ if ((to_target == _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_IMAGE_PNG) &&
+ from_format == _gdk_win32_selection_cf (GDK_WIN32_CF_INDEX_PNG)) ||
+ (to_target == _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_IMAGE_JPEG) &&
+ from_format == _gdk_win32_selection_cf (GDK_WIN32_CF_INDEX_JFIF)) ||
+ (to_target == _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_GIF) &&
+ from_format == _gdk_win32_selection_cf (GDK_WIN32_CF_INDEX_GIF)))
+ {
+ /* No transmutation needed */
+ *set_data = g_memdup (data, length);
+ *set_data_length = length;
+ }
+ else if (to_target == _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_UTF8_STRING) &&
+ from_format == CF_UNICODETEXT)
+ {
+ transmute_cf_unicodetext_to_utf8_string (data, length, set_data, set_data_length, NULL);
+ }
+ else if (to_target == _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_UTF8_STRING) &&
+ from_format == CF_TEXT)
+ {
+ transmute_cf_text_to_utf8_string (data, length, set_data, set_data_length, NULL);
+ }
+ else if (to_target == _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_IMAGE_BMP) &&
+ (from_format == CF_DIB || from_format == CF_DIBV5))
+ {
+ transmute_cf_dib_to_image_bmp (data, length, set_data, set_data_length, NULL);
+ }
+ else if (to_target == _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_TEXT_URI_LIST) &&
+ from_format == _gdk_win32_selection_cf (GDK_WIN32_CF_INDEX_CFSTR_SHELLIDLIST))
+ {
+ transmute_cf_shell_id_list_to_text_uri_list (data, length, set_data, set_data_length, NULL);
+ }
+ else
+ {
+ g_warning ("Don't know how to transmute format 0x%x to target 0x%p", from_format, to_target);
+ }
+}
+
+void
+transmute_selection_target (GdkAtom from_target,
+ UINT to_format,
+ const guchar *data,
+ gint length,
+ guchar **set_data,
+ gint *set_data_length)
+{
+ if ((from_target == _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_IMAGE_PNG) &&
+ to_format == _gdk_win32_selection_cf (GDK_WIN32_CF_INDEX_PNG)) ||
+ (from_target == _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_IMAGE_JPEG) &&
+ to_format == _gdk_win32_selection_cf (GDK_WIN32_CF_INDEX_JFIF)) ||
+ (from_target == _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_GIF) &&
+ to_format == _gdk_win32_selection_cf (GDK_WIN32_CF_INDEX_GIF)))
+ {
+ /* No conversion needed */
+ *set_data = g_memdup (data, length);
+ *set_data_length = length;
+ }
+ else if (from_target == _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_UTF8_STRING) &&
+ to_format == CF_UNICODETEXT)
+ {
+ transmute_utf8_string_to_cf_unicodetext (data, length, set_data, set_data_length, NULL);
+ }
+ else if (from_target == _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_UTF8_STRING) &&
+ to_format == CF_TEXT)
+ {
+ transmute_utf8_string_to_cf_text (data, length, set_data, set_data_length, NULL);
+ }
+ else if (from_target == _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_IMAGE_BMP) &&
+ to_format == CF_DIB)
+ {
+ transmute_image_bmp_to_cf_dib (data, length, set_data, set_data_length, NULL);
+ }
+ else if (from_target == _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_IMAGE_BMP) &&
+ to_format == CF_DIBV5)
+ {
+ transmute_image_bmp_to_cf_dib (data, length, set_data, set_data_length, NULL);
+ }
+/*
+ else if (from_target == _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_TEXT_URI_LIST) &&
+ to_format == _gdk_win32_selection_cf (GDK_WIN32_CF_INDEX_CFSTR_SHELLIDLIST))
+ {
+ transmute_text_uri_list_to_shell_id_list (data, length, set_data, set_data_length, NULL);
+ }
+*/
+ else
+ {
+ g_warning ("Don't know how to transmute from target 0x%p to format 0x%x", from_target, to_format);
+ }
+}
+
+static GdkAtom
+convert_clipboard_selection_to_targets_target (GdkWindow *requestor)
+{
+ gint fmt;
+ int i;
+ int format_count = CountClipboardFormats ();
+ GArray *targets = g_array_sized_new (FALSE, FALSE, sizeof (GdkSelTargetFormat), format_count);
+
+ for (fmt = 0; 0 != (fmt = EnumClipboardFormats (fmt)); )
+ _gdk_win32_add_format_to_targets (fmt, targets, NULL);
+
+ GDK_NOTE (DND, {
+ g_print ("... ");
+ for (i = 0; i < targets->len; i++)
+ {
+ gchar *atom_name = gdk_atom_name (g_array_index (targets, GdkSelTargetFormat, i).target);
+
+ g_print ("%s", atom_name);
+ g_free (atom_name);
+ if (i < targets->len - 1)
+ g_print (", ");
+ }
+ g_print ("\n");
+ });
+
+ if (targets->len > 0)
+ {
+ gint len = targets->len;
+ GdkAtom *targets_only = g_new0 (GdkAtom, len);
+
+ for (i = 0; i < targets->len; i++)
+ targets_only[i] = g_array_index (targets, GdkSelTargetFormat, i).target;
+
+ g_array_free (targets, TRUE);
+ selection_property_store (requestor, GDK_SELECTION_TYPE_ATOM,
+ 32, (guchar *) targets_only,
+ len * sizeof (GdkAtom));
+ return _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_GDK_SELECTION);
+ }
+ else
+ {
+ g_array_free (targets, TRUE);
+ return GDK_NONE;
+ }
+}
+
+static GdkAtom
+convert_clipboard_selection_to_target (GdkWindow *requestor,
+ GdkAtom target)
+{
+ UINT format;
+ HANDLE hdata;
+ guchar *ptr;
+ gint length;
+ gboolean transmute = FALSE;
+ GdkAtom result = _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_GDK_SELECTION);
+ gboolean found;
+ gchar *atom_name;
+
+ atom_name = gdk_atom_name (target);
+
+ for (format = 0, found = FALSE;
+ !found && 0 != (format = EnumClipboardFormats (format));
+ )
+ {
+ gboolean predef;
+ gchar *format_name = _gdk_win32_get_clipboard_format_name (format, &predef);
+
+ if (format_name == NULL)
+ continue;
+
+ found = g_strcmp0 (format_name, atom_name) == 0;
+ g_free (format_name);
+ }
+
+ g_free (atom_name);
+
+ if (format == 0)
+ {
+ gint i;
+ GArray *compat_formats = get_compatibility_formats_for_target (target);
+
+ for (i = 0; compat_formats != NULL && i < compat_formats->len; i++)
+ {
+ if (!IsClipboardFormatAvailable (g_array_index (compat_formats, GdkSelTargetFormat, i).format))
+ continue;
+
+ format = g_array_index (compat_formats, GdkSelTargetFormat, i).format;
+ transmute = g_array_index (compat_formats, GdkSelTargetFormat, i).transmute;
+ break;
+ }
+ }
+
+ if (format == 0)
+ return GDK_NONE;
+
+ if ((hdata = GetClipboardData (format)) == NULL)
+ return GDK_NONE;
+
+ if ((ptr = GlobalLock (hdata)) != NULL)
+ {
+ guchar *data = NULL;
+ gint data_len = 0;
+ length = GlobalSize (hdata);
+
+ GDK_NOTE (DND, g_print ("... format 0x%x: %d bytes\n", format, length));
+
+ if (transmute)
+ {
+ transmute_selection_format (format, target, ptr, length, &data, &data_len);
+ }
+ else
+ {
+ data = g_memdup (ptr, length);
+ data_len = length;
+ }
+
+ if (data)
+ selection_property_store (requestor, target,
+ 8, data, data_len);
+ else
+ result = GDK_NONE;
+
+ GlobalUnlock (hdata);
+ }
+
+ return result;
+}
+
+static GdkAtom
+convert_selection_with_opened_clipboard (GdkDisplay *display,
+ GdkWindow *requestor,
+ GdkAtom target,
+ guint32 time)
+{
+ GdkAtom property;
+
+ if (target == _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_TARGETS))
+ property = convert_clipboard_selection_to_targets_target (requestor);
+ else
+ property = convert_clipboard_selection_to_target (requestor, target);
+
+ return property;
+}
+
+static void
+announce_delayrendered_targets_with_opened_clipboard (GdkWin32Selection *win32_sel)
+{
+ gint i;
+ /* Announce the formats we support, but don't actually put any data out there.
+ * Other processes will send us WM_RENDERFORMAT to get the data.
+ */
+ for (i = 0; i < win32_sel->clipboard_selection_targets->len; i++)
+ {
+ GdkSelTargetFormat *fmt = &g_array_index (win32_sel->clipboard_selection_targets, GdkSelTargetFormat, i);
+
+ /* Some calls here may be duplicates, but we don't really care */
+ if (fmt->format != 0)
+ SetClipboardData (fmt->format, NULL);
+ }
+}
+
+static gboolean
+open_clipboard_timeout (gpointer data)
+{
+ GList *tmp_list, *next;
+ GdkWin32ClipboardQueueInfo *info;
+ GdkWin32Selection *win32_sel = _gdk_win32_selection_get ();
+
+ GDK_NOTE (DND, g_print ("Open clipboard timeout ticks\n"));
+
+ /* Clear out old and invalid entries */
+ for (tmp_list = clipboard_queue; tmp_list; tmp_list = next)
+ {
+ info = (GdkWin32ClipboardQueueInfo *) tmp_list->data;
+ next = g_list_next (tmp_list);
+
+ if (GDK_WINDOW_DESTROYED (info->requestor) ||
+ info->idle_time >= CLIPBOARD_IDLE_ABORT_TIME)
+ {
+ clipboard_queue = g_list_remove_link (clipboard_queue, tmp_list);
+ g_list_free (tmp_list);
+ switch (info->action)
+ {
+ case GDK_WIN32_CLIPBOARD_QUEUE_ACTION_TARGETS:
+ break;
+ case GDK_WIN32_CLIPBOARD_QUEUE_ACTION_CONVERT:
+ generate_selection_notify (info->requestor, info->selection, info->target, GDK_NONE, info->time);
+ break;
+ }
+ g_clear_object (&info->requestor);
+ g_slice_free (GdkWin32ClipboardQueueInfo, info);
+ }
+ }
+
+ if (clipboard_queue == NULL)
+ {
+ GDK_NOTE (DND, g_print ("Stopping open clipboard timer\n"));
+
+ if (win32_sel->clipboard_opened_for != INVALID_HANDLE_VALUE)
+ {
+ API_CALL (CloseClipboard, ());
+ win32_sel->clipboard_opened_for = INVALID_HANDLE_VALUE;
+ GDK_NOTE (DND, g_print ("Closed clipboard @ %s:%d\n", __FILE__, __LINE__));
+ }
+
+ return FALSE;
+ }
+
+ for (tmp_list = clipboard_queue; tmp_list; tmp_list = next)
+ {
+ GdkAtom property;
+
+ info = (GdkWin32ClipboardQueueInfo *) tmp_list->data;
+ next = g_list_next (tmp_list);
+
+ /* CONVERT works with any opened clipboard,
+ * but TARGETS needs to open the clipboard with the hande of the
+ * owner window.
+ */
+ if (info->action == GDK_WIN32_CLIPBOARD_QUEUE_ACTION_TARGETS &&
+ win32_sel->clipboard_opened_for == NULL)
+ {
+ GDK_NOTE (DND, g_print ("Need to re-open clipboard, closing\n"));
+ API_CALL (CloseClipboard, ());
+ win32_sel->clipboard_opened_for = INVALID_HANDLE_VALUE;
+ }
+
+ if (win32_sel->clipboard_opened_for == INVALID_HANDLE_VALUE)
+ {
+ if (!OpenClipboard (GDK_WINDOW_HWND (info->requestor)))
+ {
+ info->idle_time += 1;
+ continue;
+ }
+ win32_sel->clipboard_opened_for = GDK_WINDOW_HWND (info->requestor);
+ GDK_NOTE (DND, g_print ("Opened clipboard for 0x%p @ %s:%d\n", win32_sel->clipboard_opened_for, __FILE__, __LINE__));
+ }
+
+ clipboard_queue = g_list_remove_link (clipboard_queue, tmp_list);
+ g_list_free (tmp_list);
+
+ switch (info->action)
+ {
+ case GDK_WIN32_CLIPBOARD_QUEUE_ACTION_CONVERT:
+ property = convert_selection_with_opened_clipboard (info->display,
+ info->requestor,
+ info->target,
+ info->time);
+ generate_selection_notify (info->requestor,
+ GDK_SELECTION_CLIPBOARD,
+ info->target,
+ property,
+ info->time);
+ break;
+ case GDK_WIN32_CLIPBOARD_QUEUE_ACTION_TARGETS:
+ announce_delayrendered_targets_with_opened_clipboard (win32_sel);
+ break;
+ default:
+ g_assert_not_reached ();
+ }
+
+ g_clear_object (&info->requestor);
+ g_slice_free (GdkWin32ClipboardQueueInfo, info);
+ }
+
+ if (clipboard_queue != NULL)
+ return TRUE;
+
+ if (win32_sel->clipboard_opened_for != INVALID_HANDLE_VALUE)
+ {
+ API_CALL (CloseClipboard, ());
+ win32_sel->clipboard_opened_for = INVALID_HANDLE_VALUE;
+ GDK_NOTE (DND, g_print ("Closed clipboard @ %s:%d\n", __FILE__, __LINE__));
+ }
+
+ GDK_NOTE (DND, g_print ("Stopping open clipboard timer\n"));
+
+ return FALSE;
+}
+
+static void
+queue_open_clipboard (GdkWin32ClipboardQueueAction action,
+ GdkDisplay *display,
+ GdkWindow *requestor,
+ GdkAtom target,
+ guint32 time)
+{
+ guint id;
+ GList *tmp_list, *next;
+ GdkWin32ClipboardQueueInfo *info;
+ GdkWin32Selection *win32_sel = _gdk_win32_selection_get ();
+
+ for (tmp_list = clipboard_queue; tmp_list; tmp_list = next)
+ {
+ info = (GdkWin32ClipboardQueueInfo *) tmp_list->data;
+ next = g_list_next (tmp_list);
+
+ if (info->action == action &&
+ info->requestor == requestor)
+ return;
+ }
+
+ info = g_slice_new (GdkWin32ClipboardQueueInfo);
+
+ info->display = display;
+ g_set_object (&info->requestor, requestor);
+ info->selection = GDK_SELECTION_CLIPBOARD;
+ info->target = target;
+ info->idle_time = 0;
+ info->time = time;
+ info->action = action;
+
+ GDK_NOTE (DND, g_print ("Queueing open clipboard\n"));
+
+ if (win32_sel->clipboard_opened_for == INVALID_HANDLE_VALUE &&
+ clipboard_queue == NULL)
+ {
+ id = gdk_threads_add_timeout_seconds (1, (GSourceFunc) open_clipboard_timeout, NULL);
+ g_source_set_name_by_id (id, "[gdk-win32] open_clipboard_timeout");
+ GDK_NOTE (DND, g_print ("Started open clipboard timer\n"));
+ }
+
+ clipboard_queue = g_list_append (clipboard_queue, info);
+}
+
+gboolean
+_gdk_win32_display_set_selection_owner (GdkDisplay *display,
+ GdkWindow *owner,
+ GdkAtom selection,
+ guint32 time,
+ gboolean send_event)
+{
+ HWND hwnd;
+ GdkWin32Selection *win32_sel = _gdk_win32_selection_get ();
+
+ g_return_val_if_fail (selection != GDK_NONE, FALSE);
+
+ GDK_NOTE (DND, {
+ gchar *sel_name = gdk_atom_name (selection);
+
+ g_print ("gdk_selection_owner_set_for_display: %p %s\n",
+ (owner ? GDK_WINDOW_HWND (owner) : NULL),
+ sel_name);
+ g_free (sel_name);
+ });
+
+ if (selection != GDK_SELECTION_CLIPBOARD)
+ {
+ if (owner != NULL)
+ g_hash_table_insert (win32_sel->sel_owner_table, selection, GDK_WINDOW_HWND (owner));
+ else
+ g_hash_table_remove (win32_sel->sel_owner_table, selection);
+
+ return TRUE;
+ }
+
+ /* Rest of this function handles the CLIPBOARD selection */
+ if (owner != NULL)
+ {
+ if (GDK_WINDOW_DESTROYED (owner))
+ return FALSE;
+
+ hwnd = GDK_WINDOW_HWND (owner);
+ }
+ else
+ hwnd = NULL;
+
+ if (win32_sel->clipboard_opened_for != hwnd &&
+ win32_sel->clipboard_opened_for != INVALID_HANDLE_VALUE)
+ {
+ API_CALL (CloseClipboard, ());
+ win32_sel->clipboard_opened_for = INVALID_HANDLE_VALUE;
+ GDK_NOTE (DND, g_print ("Closed clipboard @ %s:%d\n", __FILE__, __LINE__));
+ }
+
+ if (!OpenClipboard (hwnd))
+ {
+ if (GetLastError () != ERROR_ACCESS_DENIED)
+ WIN32_API_FAILED ("OpenClipboard");
+
+ return FALSE;
+ }
- _ignore_destroy_clipboard = TRUE;
+ win32_sel->clipboard_opened_for = hwnd;
+ GDK_NOTE (DND, g_print ("Opened clipboard for 0x%p @ %s:%d\n", win32_sel->clipboard_opened_for, __FILE__, __LINE__));
+ win32_sel->ignore_destroy_clipboard = TRUE;
GDK_NOTE (DND, g_print ("... EmptyClipboard()\n"));
if (!API_CALL (EmptyClipboard, ()))
{
- _ignore_destroy_clipboard = FALSE;
+ win32_sel->ignore_destroy_clipboard = FALSE;
API_CALL (CloseClipboard, ());
+ win32_sel->clipboard_opened_for = INVALID_HANDLE_VALUE;
+ GDK_NOTE (DND, g_print ("Closed clipboard @ %s:%d\n", __FILE__, __LINE__));
return FALSE;
}
- _ignore_destroy_clipboard = FALSE;
+ win32_sel->ignore_destroy_clipboard = FALSE;
- if (!API_CALL (CloseClipboard, ()))
- return FALSE;
+ /* Any queued clipboard operations were just made pointless
+ * by EmptyClipboard().
+ */
+ _gdk_win32_clear_clipboard_queue ();
- if (owner != NULL)
+ /* This is kind of risky, but we don't close the clipboard
+ * to ensure that it's still open when GDK_SELECTION_REQUEST
+ * is handled.
+ */
+ if (owner == NULL)
{
- /* Send ourselves a selection request message so that
- * gdk_property_change will be called to store the clipboard
- * data.
- */
- GDK_NOTE (DND, g_print ("... sending GDK_SELECTION_REQUEST to ourselves\n"));
- tmp_event.selection.type = GDK_SELECTION_REQUEST;
- tmp_event.selection.window = owner;
- tmp_event.selection.send_event = FALSE;
- tmp_event.selection.selection = selection;
- tmp_event.selection.target = _utf8_string;
- tmp_event.selection.property = _gdk_selection;
- tmp_event.selection.requestor = gdk_win32_handle_table_lookup (hwnd);
- tmp_event.selection.time = time;
-
- gdk_event_put (&tmp_event);
+ if (!API_CALL (CloseClipboard, ()))
+ return FALSE;
+ GDK_NOTE (DND, g_print ("Closed clipboard @ %s:%d\n", __FILE__, __LINE__));
+ win32_sel->clipboard_opened_for = INVALID_HANDLE_VALUE;
}
+ send_targets_request (time);
+
return TRUE;
}
GdkAtom selection)
{
GdkWindow *window;
+ HWND selection_owner;
+ GdkWin32Selection *win32_sel = _gdk_win32_selection_get ();
g_return_val_if_fail (selection != GDK_NONE, NULL);
if (selection == GDK_SELECTION_CLIPBOARD)
- {
- HWND owner = GetClipboardOwner ();
-
- if (owner == NULL)
- return NULL;
-
- return gdk_win32_handle_table_lookup (owner);
- }
+ selection_owner = GetClipboardOwner ();
+ else
+ selection_owner = g_hash_table_lookup (win32_sel->sel_owner_table, selection);
- window = gdk_win32_window_lookup_for_display (display,
- g_hash_table_lookup (sel_owner_table, selection));
+ if (selection_owner)
+ window = gdk_win32_window_lookup_for_display (display,
+ selection_owner);
+ else
+ window = NULL;
GDK_NOTE (DND, {
gchar *sel_name = gdk_atom_name (selection);
return window;
}
-static void
-generate_selection_notify (GdkWindow *requestor,
- GdkAtom selection,
- GdkAtom target,
- GdkAtom property,
- guint32 time)
+static GdkAtom
+convert_dnd_selection_to_target (GdkAtom target,
+ GdkWindow *requestor)
{
- GdkEvent tmp_event;
+ GdkWin32Selection *win32_sel = _gdk_win32_selection_get ();
+ UINT format;
+ gint i, with_transmute;
+ guchar *ptr;
+ gint length;
+ gboolean transmute = FALSE;
+ GdkWin32DragContext *context_win32;
+ FORMATETC fmt;
+ STGMEDIUM storage;
+ HRESULT hr;
+ GdkAtom result = _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_OLE2_DND);
+
+ g_assert (win32_sel->target_drag_context != NULL);
+ g_assert (win32_sel->dnd_data_object_target != NULL);
+
+ context_win32 = GDK_WIN32_DRAG_CONTEXT (win32_sel->target_drag_context);
+
+ fmt.ptd = NULL;
+ fmt.dwAspect = DVASPECT_CONTENT;
+ fmt.lindex = -1;
+ fmt.tymed = TYMED_HGLOBAL;
+
+ for (format = 0, with_transmute = 0; format == 0 && with_transmute < 2; with_transmute++)
+ {
+ for (i = 0;
+ i < context_win32->droptarget_format_target_map->len;
+ i++)
+ {
+ GdkSelTargetFormat selformat = g_array_index (context_win32->droptarget_format_target_map, GdkSelTargetFormat, i);
- tmp_event.selection.type = GDK_SELECTION_NOTIFY;
- tmp_event.selection.window = requestor;
- tmp_event.selection.send_event = FALSE;
- tmp_event.selection.selection = selection;
- tmp_event.selection.target = target;
- tmp_event.selection.property = property;
- tmp_event.selection.requestor = 0;
- tmp_event.selection.time = time;
+ if (selformat.target != target ||
+ selformat.transmute != (with_transmute == 0 ? FALSE : TRUE))
+ continue;
- gdk_event_put (&tmp_event);
+ fmt.cfFormat = selformat.format;
+
+ hr = IDataObject_QueryGetData (win32_sel->dnd_data_object_target, &fmt);
+
+ if (!SUCCEEDED (hr) || hr != S_OK)
+ continue;
+
+ format = selformat.format;
+ transmute = selformat.transmute;
+ break;
+ }
+ }
+
+ if (format == 0)
+ return GDK_NONE;
+
+ hr = IDataObject_GetData (win32_sel->dnd_data_object_target, &fmt, &storage);
+
+ if (!SUCCEEDED (hr) || hr != S_OK)
+ return GDK_NONE;
+
+ if ((ptr = GlobalLock (storage.hGlobal)) != NULL)
+ {
+ guchar *data = NULL;
+ gint data_len = 0;
+
+ SetLastError (0);
+ length = GlobalSize (storage.hGlobal);
+
+ if (GetLastError () == NO_ERROR)
+ {
+ if (transmute)
+ {
+ transmute_selection_format (format, target, ptr, length, &data, &data_len);
+ }
+ else
+ {
+ data = g_memdup (ptr, length);
+ data_len = length;
+ }
+
+ if (data)
+ selection_property_store (requestor, target, 8,
+ data, data_len);
+ else
+ result = GDK_NONE;
+ }
+ else
+ result = GDK_NONE;
+
+ GlobalUnlock (storage.hGlobal);
+ }
+ else
+ result = GDK_NONE;
+
+ ReleaseStgMedium (&storage);
+
+ return result;
}
void
GdkAtom target,
guint32 time)
{
- HGLOBAL hdata;
- GdkAtom property = _gdk_selection;
+ GdkWin32Selection *win32_sel = _gdk_win32_selection_get ();
+ GdkAtom property = _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_GDK_SELECTION);
g_return_if_fail (selection != GDK_NONE);
g_return_if_fail (requestor != NULL);
g_free (tgt_name);
});
- if (selection == GDK_SELECTION_CLIPBOARD && target == _targets)
+ if (selection == GDK_SELECTION_CLIPBOARD)
{
- gint ntargets, fmt;
- GdkAtom *targets;
- gboolean has_text = FALSE;
- gboolean has_png = FALSE;
- gboolean has_bmp = FALSE;
-
- if (!API_CALL (OpenClipboard, (GDK_WINDOW_HWND (requestor))))
- return;
-
- targets = g_new (GdkAtom, CountClipboardFormats ());
- ntargets = 0;
-
- for (fmt = 0; 0 != (fmt = EnumClipboardFormats (fmt)); )
- {
- if (fmt == _cf_png)
- {
- targets[ntargets++] = _image_png;
- has_png = TRUE;
- }
- }
-
- for (fmt = 0; 0 != (fmt = EnumClipboardFormats (fmt)); )
- {
- gchar sFormat[80];
-
- if (fmt == CF_UNICODETEXT || fmt == CF_TEXT)
- {
- /* Advertise text to GDK always as UTF8_STRING */
- if (!has_text)
- targets[ntargets++] = _utf8_string;
- has_text = TRUE;
- }
- else if (fmt == _cf_png)
- {
- /* Already handled above */
- }
- else if (fmt == CF_DIB ||
- fmt == CF_DIBV5)
- {
- /* Don't bother telling that a bitmap is present if there is
- * also PNG, which is much more reliable in transferring
- * transparency.
- */
- if (!has_bmp && !has_png)
- targets[ntargets++] = _image_bmp;
- has_bmp = TRUE;
- }
- else if (fmt == _cf_jfif)
- {
- /* Ditto for JPEG */
- if (!has_png)
- targets[ntargets++] = _image_jpeg;
- }
- else if (fmt == _cf_gif)
- {
- /* Ditto for GIF.
- */
- if (!has_png)
- targets[ntargets++] = _image_gif;
- }
- else if (GetClipboardFormatName (fmt, sFormat, 80) > 0)
- {
- if (strcmp (sFormat, "image/bmp") == 0 ||
- strcmp (sFormat, "image/x-bmp") == 0 ||
- strcmp (sFormat, "image/x-MS-bmp") == 0 ||
- strcmp (sFormat, "image/x-icon") == 0 ||
- strcmp (sFormat, "image/x-ico") == 0 ||
- strcmp (sFormat, "image/x-win-bitmap") == 0)
- {
- /* Ignore these (from older GTK+ versions
- * presumably), as the same image in the CF_DIB
- * format will also be on the clipboard anyway.
- */
- }
- else
- targets[ntargets++] = gdk_atom_intern (sFormat, FALSE);
+ if (win32_sel->clipboard_opened_for != INVALID_HANDLE_VALUE ||
+ OpenClipboard (GDK_WINDOW_HWND (requestor)))
+ {
+ if (win32_sel->clipboard_opened_for == INVALID_HANDLE_VALUE)
+ {
+ win32_sel->clipboard_opened_for = GDK_WINDOW_HWND (requestor);
+ GDK_NOTE (DND, g_print ("Opened clipboard for 0x%p @ %s:%d\n", win32_sel->clipboard_opened_for, __FILE__, __LINE__));
}
- }
-
- API_CALL (CloseClipboard, ());
-
- GDK_NOTE (DND, {
- int i;
- g_print ("... ");
- for (i = 0; i < ntargets; i++)
- {
- gchar *atom_name = gdk_atom_name (targets[i]);
-
- g_print ("%s", atom_name);
- g_free (atom_name);
- if (i < ntargets - 1)
- g_print (", ");
- }
- g_print ("\n");
- });
-
- if (ntargets > 0)
- selection_property_store (requestor, GDK_SELECTION_TYPE_ATOM,
- 32, (guchar *) targets,
- ntargets * sizeof (GdkAtom));
+ queue_open_clipboard (GDK_WIN32_CLIPBOARD_QUEUE_ACTION_CONVERT, display, requestor, target, time);
+ open_clipboard_timeout (NULL);
+ }
else
- property = GDK_NONE;
+ {
+ queue_open_clipboard (GDK_WIN32_CLIPBOARD_QUEUE_ACTION_CONVERT, display, requestor, target, time);
+ /* Do not generate a selection notify message */
+ return;
+ }
}
- else if (selection == GDK_SELECTION_CLIPBOARD && target == _utf8_string)
+ else if (selection == _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_DROPFILES_DND))
{
- /* Converting the CLIPBOARD selection means he wants the
- * contents of the clipboard. Get the clipboard data, and store
- * it for later.
+ /* This means he wants the names of the dropped files.
+ * gdk_dropfiles_filter already has stored the text/uri-list
+ * data temporarily in dropfiles_prop.
*/
- if (!API_CALL (OpenClipboard, (GDK_WINDOW_HWND (requestor))))
- return;
-
- if ((hdata = GetClipboardData (CF_UNICODETEXT)) != NULL)
+ if (win32_sel->dropfiles_prop != NULL)
{
- wchar_t *ptr, *p, *q;
- gchar *data;
- glong length, wclen;
-
- if ((ptr = GlobalLock (hdata)) != NULL)
- {
- length = GlobalSize (hdata);
-
- GDK_NOTE (DND, g_print ("... CF_UNICODETEXT: %ld bytes\n",
- length));
-
- /* Strip out \r */
- p = ptr;
- q = ptr;
- wclen = 0;
- while (p < ptr + length / 2)
- {
- if (*p != '\r')
- {
- *q++ = *p;
- wclen++;
- }
- p++;
- }
-
- data = g_utf16_to_utf8 (ptr, wclen, NULL, NULL, NULL);
-
- if (data)
- selection_property_store (requestor, _utf8_string, 8,
- (guchar *) data, strlen (data) + 1);
- GlobalUnlock (hdata);
- }
+ selection_property_store
+ (requestor, win32_sel->dropfiles_prop->target, win32_sel->dropfiles_prop->bitness,
+ win32_sel->dropfiles_prop->data, win32_sel->dropfiles_prop->length);
+ g_clear_pointer (&win32_sel->dropfiles_prop, g_free);
}
- else
- property = GDK_NONE;
-
- API_CALL (CloseClipboard, ());
}
- else if (selection == GDK_SELECTION_CLIPBOARD && target == _image_bmp)
+ else if (selection == _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_OLE2_DND))
{
- if (!API_CALL (OpenClipboard, (GDK_WINDOW_HWND (requestor))))
- return;
+ property = convert_dnd_selection_to_target (target, requestor);
+ }
+ else
+ property = GDK_NONE;
- if ((hdata = GetClipboardData (CF_DIB)) != NULL)
- {
- BITMAPINFOHEADER *bi;
+ /* Generate a selection notify message so that we actually fetch the
+ * data (if property == GDK_SELECTION) or indicating failure (if
+ * property == GDK_NONE).
+ */
+ generate_selection_notify (requestor, selection, target, property, time);
+}
- if ((bi = GlobalLock (hdata)) != NULL)
- {
- /* Need to add a BMP file header so gdk-pixbuf can load
- * it.
- *
- * If the data is from Mozilla Firefox or IE7, and
- * starts with an "old fashioned" BITMAPINFOHEADER,
- * i.e. with biSize==40, and biCompression == BI_RGB and
- * biBitCount==32, we assume that the "extra" byte in
- * each pixel in fact is alpha.
- *
- * The gdk-pixbuf bmp loader doesn't trust 32-bit BI_RGB
- * bitmaps to in fact have alpha, so we have to convince
- * it by changing the bitmap header to a version 5
- * BI_BITFIELDS one with explicit alpha mask indicated.
- *
- * The RGB bytes that are in bitmaps on the clipboard
- * originating from Firefox or IE7 seem to be
- * premultiplied with alpha. The gdk-pixbuf bmp loader
- * of course doesn't expect that, so we have to undo the
- * premultiplication before feeding the bitmap to the
- * bmp loader.
- *
- * Note that for some reason the bmp loader used to want
- * the alpha bytes in its input to actually be
- * 255-alpha, but here we assume that this has been
- * fixed before this is committed.
- */
- BITMAPFILEHEADER *bf;
- gpointer data;
- gint data_length = GlobalSize (hdata);
- gint new_length;
- gboolean make_dibv5 = FALSE;
-
- GDK_NOTE (DND, g_print ("... CF_DIB: %d bytes\n", data_length));
-
- if (bi->biSize == sizeof (BITMAPINFOHEADER) &&
- bi->biPlanes == 1 &&
- bi->biBitCount == 32 &&
- bi->biCompression == BI_RGB &&
-#if 0
- /* Maybe check explicitly for Mozilla or IE7?
- *
- * If the clipboard format
- * application/x-moz-nativeimage is present, that is
- * a reliable indicator that the data is offered by
- * Mozilla one would think. For IE7,
- * UniformResourceLocatorW is presumably not that
- * uniqie, so probably need to do some
- * GetClipboardOwner(), GetWindowThreadProcessId(),
- * OpenProcess(), GetModuleFileNameEx() dance to
- * check?
- */
- (IsClipboardFormatAvailable
- (RegisterClipboardFormat ("application/x-moz-nativeimage")) ||
- IsClipboardFormatAvailable
- (RegisterClipboardFormat ("UniformResourceLocatorW"))) &&
-#endif
- TRUE)
- {
- /* We turn the BITMAPINFOHEADER into a
- * BITMAPV5HEADER before feeding it to gdk-pixbuf.
- */
- new_length = (data_length +
- sizeof (BITMAPFILEHEADER) +
- (sizeof (BITMAPV5HEADER) - sizeof (BITMAPINFOHEADER)));
- make_dibv5 = TRUE;
- }
- else
- {
- new_length = data_length + sizeof (BITMAPFILEHEADER);
- }
-
- data = g_try_malloc (new_length);
-
- if (data)
- {
- bf = (BITMAPFILEHEADER *)data;
- bf->bfType = 0x4d42; /* "BM" */
- bf->bfSize = new_length;
- bf->bfReserved1 = 0;
- bf->bfReserved2 = 0;
-
- if (make_dibv5)
- {
- BITMAPV5HEADER *bV5 = (BITMAPV5HEADER *) ((char *) data + sizeof (BITMAPFILEHEADER));
- guchar *p;
- guint i;
-
- bV5->bV5Size = sizeof (BITMAPV5HEADER);
- bV5->bV5Width = bi->biWidth;
- bV5->bV5Height = bi->biHeight;
- bV5->bV5Planes = 1;
- bV5->bV5BitCount = 32;
- bV5->bV5Compression = BI_BITFIELDS;
- bV5->bV5SizeImage = 4 * bV5->bV5Width * ABS (bV5->bV5Height);
- bV5->bV5XPelsPerMeter = bi->biXPelsPerMeter;
- bV5->bV5YPelsPerMeter = bi->biYPelsPerMeter;
- bV5->bV5ClrUsed = 0;
- bV5->bV5ClrImportant = 0;
- /* Now the added mask fields */
- bV5->bV5RedMask = 0x00ff0000;
- bV5->bV5GreenMask = 0x0000ff00;
- bV5->bV5BlueMask = 0x000000ff;
- bV5->bV5AlphaMask = 0xff000000;
- ((char *) &bV5->bV5CSType)[3] = 's';
- ((char *) &bV5->bV5CSType)[2] = 'R';
- ((char *) &bV5->bV5CSType)[1] = 'G';
- ((char *) &bV5->bV5CSType)[0] = 'B';
- /* Ignore colorspace and profile fields */
- bV5->bV5Intent = LCS_GM_GRAPHICS;
- bV5->bV5Reserved = 0;
-
- bf->bfOffBits = (sizeof (BITMAPFILEHEADER) +
- bV5->bV5Size);
-
- p = ((guchar *) data) + sizeof (BITMAPFILEHEADER) + sizeof (BITMAPV5HEADER);
- memcpy (p, ((char *) bi) + bi->biSize,
- data_length - sizeof (BITMAPINFOHEADER));
-
- for (i = 0; i < bV5->bV5SizeImage/4; i++)
- {
- if (p[3] != 0)
- {
- gdouble inverse_alpha = 255./p[3];
-
- p[0] = p[0] * inverse_alpha + 0.5;
- p[1] = p[1] * inverse_alpha + 0.5;
- p[2] = p[2] * inverse_alpha + 0.5;
- }
-
- p += 4;
- }
- }
- else
- {
- bf->bfOffBits = (sizeof (BITMAPFILEHEADER) +
- bi->biSize +
- bi->biClrUsed * sizeof (RGBQUAD));
-
- if (bi->biCompression == BI_BITFIELDS && bi->biBitCount >= 16)
- {
- /* Screenshots taken with PrintScreen or
- * Alt + PrintScreen are found on the clipboard in
- * this format. In this case the BITMAPINFOHEADER is
- * followed by three DWORD specifying the masks of the
- * red green and blue components, so adjust the offset
- * accordingly. */
- bf->bfOffBits += (3 * sizeof (DWORD));
- }
-
- memcpy ((char *) data + sizeof (BITMAPFILEHEADER),
- bi,
- data_length);
- }
-
- selection_property_store (requestor, _image_bmp, 8,
- data, new_length);
- }
- GlobalUnlock (hdata);
- }
- }
+void
+_gdk_win32_selection_property_change (GdkWin32Selection *win32_sel,
+ GdkWindow *window,
+ GdkAtom property,
+ GdkAtom type,
+ gint format,
+ GdkPropMode mode,
+ const guchar *data,
+ gint nelements)
+{
+ if (property == _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_GDK_SELECTION) &&
+ type == GDK_SELECTION_TYPE_ATOM) /* implies target == _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_TARGETS) */
+ {
+ if (win32_sel->clipboard_opened_for == INVALID_HANDLE_VALUE &&
+ OpenClipboard (GDK_WINDOW_HWND (window)))
+ {
+ win32_sel->clipboard_opened_for = GDK_WINDOW_HWND (window);
+ GDK_NOTE (DND, g_print ("Opened clipboard for 0x%p @ %s:%d\n", win32_sel->clipboard_opened_for, __FILE__, __LINE__));
+ }
- API_CALL (CloseClipboard, ());
+ if (win32_sel->clipboard_opened_for == INVALID_HANDLE_VALUE)
+ {
+ queue_open_clipboard (GDK_WIN32_CLIPBOARD_QUEUE_ACTION_TARGETS, NULL, window, type, GDK_CURRENT_TIME);
+ return;
+ }
+ else
+ {
+ queue_open_clipboard (GDK_WIN32_CLIPBOARD_QUEUE_ACTION_TARGETS, NULL, window, type, GDK_CURRENT_TIME);
+ open_clipboard_timeout (NULL);
+ }
}
- else if (selection == GDK_SELECTION_CLIPBOARD)
+ else if (mode == GDK_PROP_MODE_REPLACE &&
+ (win32_sel->property_change_data == NULL ||
+ win32_sel->property_change_format == 0))
{
- gchar *mapped_target_name;
- UINT fmt = 0;
-
- if (!API_CALL (OpenClipboard, (GDK_WINDOW_HWND (requestor))))
- return;
-
- mapped_target_name = get_mapped_gdk_atom_name (target);
+ g_warning ("Setting selection property with 0x%p == NULL or 0x%x == 0", win32_sel->property_change_data, win32_sel->property_change_format);
+ }
+ else if (mode == GDK_PROP_MODE_REPLACE &&
+ win32_sel->property_change_data != NULL &&
+ win32_sel->property_change_format != 0)
+ {
+ guchar *set_data = NULL;
+ gint set_data_length = 0;
+ gint byte_length = format / 8 * nelements;
+
+ if (win32_sel->property_change_transmute)
+ transmute_selection_target (type,
+ win32_sel->property_change_format,
+ data,
+ byte_length,
+ &set_data,
+ &set_data_length);
+ else
+ {
+ set_data_length = byte_length;
+ set_data = g_memdup (data, set_data_length);
+ }
- /* Check if it's available. We could simply call
- * GetClipboardData (RegisterClipboardFormat (targetname)), but
- * the global custom format ID space is limited,
- * (0xC000~0xFFFF), and we better not waste an format ID if we
- * are just a requestor.
- */
- for ( ; 0 != (fmt = EnumClipboardFormats (fmt)); )
+ if (set_data != NULL && set_data_length > 0)
{
- char sFormat[80];
+ HGLOBAL hdata;
- if (GetClipboardFormatName (fmt, sFormat, 80) > 0 &&
- strcmp (sFormat, mapped_target_name) == 0)
+ if ((hdata = GlobalAlloc (GMEM_MOVEABLE, set_data_length)) != 0)
{
- if ((hdata = GetClipboardData (fmt)) != NULL)
- {
- /* Simply get it without conversion */
- guchar *ptr;
- gint length;
-
- if ((ptr = GlobalLock (hdata)) != NULL)
- {
- length = GlobalSize (hdata);
-
- GDK_NOTE (DND, g_print ("... %s: %d bytes\n", mapped_target_name, length));
-
- selection_property_store (requestor, target, 8,
- g_memdup (ptr, length), length);
- GlobalUnlock (hdata);
- break;
- }
- }
+ gchar *ucptr;
+ win32_sel->property_change_data->tymed = TYMED_HGLOBAL;
+ win32_sel->property_change_data->pUnkForRelease = NULL;
+ win32_sel->property_change_data->hGlobal = hdata;
+ ucptr = GlobalLock (hdata);
+ memcpy (ucptr, set_data, set_data_length);
+ GlobalUnlock (hdata);
+ }
+ else
+ {
+ WIN32_API_FAILED ("GlobalAlloc");
}
+
+ g_free (set_data);
}
- g_free (mapped_target_name);
- API_CALL (CloseClipboard, ());
}
- else if (selection == _gdk_win32_dropfiles)
+ else
{
- /* This means he wants the names of the dropped files.
- * gdk_dropfiles_filter already has stored the text/uri-list
- * data temporarily in dropfiles_prop.
- */
- if (dropfiles_prop != NULL)
- {
- selection_property_store
- (requestor, dropfiles_prop->type, dropfiles_prop->format,
- dropfiles_prop->data, dropfiles_prop->length);
- g_free (dropfiles_prop);
- dropfiles_prop = NULL;
- }
+ gchar *prop_name = gdk_atom_name (property);
+ gchar *type_name = gdk_atom_name (type);
+ gchar *datastring = _gdk_win32_data_to_string (data, MIN (10, format*nelements/8));
+
+ g_warning ("Unsupported property change on window 0x%p, %s property %s, %d-bit, target 0x%x of %d bytes: %s",
+ window,
+ (mode == GDK_PROP_MODE_REPLACE ? "REPLACE" :
+ (mode == GDK_PROP_MODE_PREPEND ? "PREPEND" :
+ (mode == GDK_PROP_MODE_APPEND ? "APPEND" :
+ "???"))),
+ prop_name,
+ format,
+ type_name,
+ nelements,
+ datastring);
+ g_free (datastring);
+ g_free (prop_name);
+ g_free (type_name);
}
- else
- property = GDK_NONE;
-
- /* Generate a selection notify message so that we actually fetch the
- * data (if property == _gdk_selection) or indicating failure (if
- * property == GDK_NONE).
- */
- generate_selection_notify (requestor, selection, target, property, time);
}
gint
GdkAtom *ret_type,
gint *ret_format)
{
+ GdkWin32Selection *win32_sel = _gdk_win32_selection_get ();
GdkSelProp *prop;
g_return_val_if_fail (requestor != NULL, 0);
GDK_NOTE (DND, g_print ("gdk_selection_property_get: %p",
GDK_WINDOW_HWND (requestor)));
- prop = g_hash_table_lookup (sel_prop_table, GDK_WINDOW_HWND (requestor));
+ prop = g_hash_table_lookup (win32_sel->sel_prop_table, GDK_WINDOW_HWND (requestor));
if (prop == NULL)
{
memmove (*data, prop->data, prop->length);
GDK_NOTE (DND, {
- gchar *type_name = gdk_atom_name (prop->type);
+ gchar *type_name = gdk_atom_name (prop->target);
- g_print (" %s format:%d length:%"G_GSIZE_FORMAT"\n", type_name, prop->format, prop->length);
+ g_print (" %s format:%d length:%"G_GSIZE_FORMAT"\n", type_name, prop->bitness, prop->length);
g_free (type_name);
});
if (ret_type)
- *ret_type = prop->type;
+ *ret_type = prop->target;
if (ret_format)
- *ret_format = prop->format;
+ *ret_format = prop->bitness;
return prop->length;
}
if (encoding == GDK_TARGET_STRING)
source_charset = g_strdup ("ISO-8859-1");
- else if (encoding == _utf8_string)
+ else if (encoding == _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_UTF8_STRING))
source_charset = g_strdup ("UTF-8");
else
source_charset = gdk_atom_name (encoding);
{
return make_list ((gchar *)text, length, TRUE, list);
}
- else if (encoding == _utf8_string)
+ else if (encoding == _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_UTF8_STRING))
{
return make_list ((gchar *)text, length, FALSE, list);
}
}
}
-gint
-gdk_string_to_compound_text_for_display (GdkDisplay *display,
- const gchar *str,
- GdkAtom *encoding,
- gint *format,
- guchar **ctext,
- gint *length)
+gchar *
+_gdk_win32_display_utf8_to_string_target (GdkDisplay *display,
+ const gchar *str)
{
- g_return_val_if_fail (str != NULL, 0);
- g_return_val_if_fail (length >= 0, 0);
+ return _gdk_utf8_to_string_target_internal (str, strlen (str));
+}
+
+void
+gdk_win32_selection_clear_targets (GdkDisplay *display,
+ GdkAtom selection)
+{
+ GdkWin32Selection *win32_sel = _gdk_win32_selection_get ();
- GDK_NOTE (DND, g_print ("gdk_string_to_compound_text_for_display: %.20s\n", str));
+ if (selection == _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_OLE2_DND) ||
+ selection == _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_LOCAL_DND_SELECTION))
+ {
+ g_array_set_size (win32_sel->dnd_selection_targets, 0);
+ }
+ else if (selection == GDK_SELECTION_CLIPBOARD)
+ {
+ g_array_set_size (win32_sel->clipboard_selection_targets, 0);
+ }
+ else if (selection == GDK_SELECTION_PRIMARY)
+ {
+ /* Do nothing */
+ }
+ else
+ {
+ gchar *sel_name = gdk_atom_name (selection);
- /* Always fail on Win32. No COMPOUND_TEXT support. */
+ g_warning ("Unsupported generic selection %s (0x%p)", sel_name, selection);
+ g_free (sel_name);
+ }
+}
- if (encoding)
- *encoding = GDK_NONE;
+gint
+_gdk_win32_add_target_to_selformats (GdkAtom target,
+ GArray *array)
+{
+ gint added_count = 0;
+ gchar *target_name;
+ wchar_t *target_name_w;
+ GdkSelTargetFormat fmt;
+ gint i;
+ GArray *compatibility_formats;
+ gint starting_point;
- if (format)
- *format = 0;
+ for (i = 0; i < array->len; i++)
+ if (g_array_index (array, GdkSelTargetFormat, i).target == target)
+ break;
- if (ctext)
- *ctext = NULL;
+ /* Don't put duplicates into the array */
+ if (i < array->len)
+ return added_count;
- if (length)
- *length = 0;
+ if (target == _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_TARGETS) ||
+ target == _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_COMPOUND_TEXT) || target == _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_SAVE_TARGETS))
+ {
+ /* Add the "we don't really support transferring that to
+ * other processes" format, just to keep the taget around.
+ */
+ fmt.target = target;
+ fmt.format = 0;
+ fmt.transmute = FALSE;
+ g_array_append_val (array, fmt);
+ added_count += 1;
+ return added_count;
+ }
- return -1;
-}
+ /* Only check the newly-added pairs for duplicates,
+ * all the ones that exist right now have different targets.
+ */
+ starting_point = array->len;
-gchar *
-_gdk_win32_display_utf8_to_string_target (GdkDisplay *display,
- const gchar *str)
-{
- return _gdk_utf8_to_string_target_internal (str, strlen (str));
-}
+ target_name = gdk_atom_name (target);
+ target_name_w = g_utf8_to_utf16 (target_name, -1, NULL, NULL, NULL);
+ g_free (target_name);
-gboolean
-gdk_utf8_to_compound_text_for_display (GdkDisplay *display,
- const gchar *str,
- GdkAtom *encoding,
- gint *format,
- guchar **ctext,
- gint *length)
-{
- g_return_val_if_fail (str != NULL, FALSE);
+ if (target_name_w == NULL)
+ return added_count;
- GDK_NOTE (DND, g_print ("gdk_utf8_to_compound_text_for_display: %.20s\n", str));
+ fmt.format = RegisterClipboardFormatW (target_name_w);
+ GDK_NOTE (DND, g_print ("Registered clipboard format %S as 0x%x\n", target_name_w, fmt.format));
+ g_free (target_name_w);
+ fmt.target = target;
+ fmt.transmute = FALSE;
- /* Always fail on Win32. No COMPOUND_TEXT support. */
+ /* Add the "as-is" format */
+ g_array_append_val (array, fmt);
+ added_count += 1;
- if (encoding)
- *encoding = GDK_NONE;
+ compatibility_formats = get_compatibility_formats_for_target (target);
+ for (i = 0; compatibility_formats != NULL && i < compatibility_formats->len; i++)
+ {
+ gint j;
- if (format)
- *format = 0;
+ fmt = g_array_index (compatibility_formats, GdkSelTargetFormat, i);
- if (ctext)
- *ctext = NULL;
+ /* Don't put duplicates into the array */
+ for (j = starting_point; j < array->len; j++)
+ if (g_array_index (array, GdkSelTargetFormat, j).format == fmt.format)
+ break;
- if (length)
- *length = 0;
+ if (j < array->len)
+ continue;
- return FALSE;
-}
+ /* Add a compatibility format */
+ g_array_append_val (array, fmt);
+ added_count += 1;
+ }
-void
-gdk_free_compound_text (guchar *ctext)
-{
- /* As we never generate anything claimed to be COMPOUND_TEXT, this
- * should never be called. Or if it is called, ctext should be the
- * NULL returned for conversions to COMPOUND_TEXT above.
- */
- g_return_if_fail (ctext == NULL);
+ return added_count;
}
/* This function is called from gtk_selection_add_target() and
* gtk_selection_add_targets() in gtkselection.c. It is this function
* that takes care of setting those clipboard formats for which we use
- * delayed rendering. Formats copied directly to the clipboard are
- * handled in gdk_property_change() in gdkproperty-win32.c.
+ * delayed rendering (that is, all formats, as we use delayed rendering
+ * for everything). This function only registers the formats, but does
+ * not announce them as supported. That is handled as a special case
+ * in gdk_window_property_change().
+ *
+ * Implementation detail:
+ * This function will be called repeatedly, every time the PRIMARY selection changes.
+ * It will also be called immediately before the CLIPBOARD selection changes.
+ * We let GTK+ handle the PRIMARY selection internally and do nothing here
+ * (therefore it's not possible to middle-click-paste between processes,
+ * unless one process deliberately puts PRIMARY selection contents into
+ * CLIPBOARD selection, and the other process does paste on middle-click).
*/
-
void
gdk_win32_selection_add_targets (GdkWindow *owner,
GdkAtom selection,
gint n_targets,
GdkAtom *targets)
{
- HWND hwnd = NULL;
- gboolean has_image = FALSE;
+ GdkWin32Selection *win32_sel = _gdk_win32_selection_get ();
gint i;
GDK_NOTE (DND, {
g_print ("\n");
});
- if (selection != GDK_SELECTION_CLIPBOARD)
- return;
-
- if (owner != NULL)
+ if (selection == _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_OLE2_DND) ||
+ selection == _gdk_win32_selection_atom (GDK_WIN32_ATOM_INDEX_LOCAL_DND_SELECTION) ||
+ selection == GDK_SELECTION_CLIPBOARD)
{
- if (GDK_WINDOW_DESTROYED (owner))
- return;
- hwnd = GDK_WINDOW_HWND (owner);
- }
-
- if (!API_CALL (OpenClipboard, (hwnd)))
- return;
-
- /* We have a very simple strategy: If some kind of pixmap image
- * format is being added, actually advertise just PNG and DIB. PNG
- * is our preferred format because it can losslessly represent any
- * image that gdk-pixbuf formats in general can, even with alpha,
- * unambiguously. CF_DIB is also advertised because of the general
- * support for it in Windows software, but note that alpha won't be
- * handled.
- */
- for (i = 0; !has_image && i < n_targets; ++i)
- {
- UINT cf;
- gchar *target_name;
- int j;
-
- for (j = 0; j < n_known_pixbuf_formats; j++)
- if (targets[i] == known_pixbuf_formats[j])
- {
- if (!has_image)
- {
- GDK_NOTE (DND, g_print ("... SetClipboardData(PNG,NULL)\n"));
- SetClipboardData (_cf_png, NULL);
-
- GDK_NOTE (DND, g_print ("... SetClipboardData(CF_DIB,NULL)\n"));
- SetClipboardData (CF_DIB, NULL);
-
- has_image = TRUE;
- }
- break;
- }
-
- /* If it is one of the pixmap formats, already handled or not
- * needed.
- */
- if (j < n_known_pixbuf_formats)
- continue;
-
- /* We don't bother registering and advertising clipboard formats
- * that are X11 specific or no non-GTK+ apps will have ever
- * heard of, and when there are equivalent clipboard formats
- * that are commonly used.
- */
- if (targets[i] == _save_targets ||
- targets[i] == _utf8_string ||
- targets[i] == GDK_TARGET_STRING ||
- targets[i] == _compound_text ||
- targets[i] == _text ||
- targets[i] == text_plain_charset_utf_8 ||
- targets[i] == text_plain_charset_CP1252 ||
- targets[i] == text_plain)
- continue;
-
- target_name = gdk_atom_name (targets[i]);
-
- if (g_str_has_prefix (target_name, "text/plain;charset="))
- {
- g_free (target_name);
- continue;
- }
+ GArray *fmts = NULL;
+ gint added_count = 0;
- cf = RegisterClipboardFormat (target_name);
-
- g_hash_table_replace (_format_atom_table,
- GINT_TO_POINTER (cf),
- targets[i]);
+ if (selection == GDK_SELECTION_CLIPBOARD)
+ fmts = win32_sel->clipboard_selection_targets;
+ else
+ fmts = win32_sel->dnd_selection_targets;
- GDK_NOTE (DND, g_print ("... SetClipboardData(%s,NULL)\n",
- _gdk_win32_cf_to_string (cf)));
- SetClipboardData (cf, NULL);
+ for (i = 0; i < n_targets; i++)
+ added_count += _gdk_win32_add_target_to_selformats (targets[i], fmts);
- g_free (target_name);
+ /* Re-announce our list of supported formats */
+ if (added_count > 0)
+ send_targets_request (GDK_CURRENT_TIME);
}
- API_CALL (CloseClipboard, ());
-}
-
-/* Convert from types such as "image/jpg" or "image/png" to DIB using
- * gdk-pixbuf so that image copied from GTK+ apps can be pasted in
- * native apps like mspaint.exe
- */
-HGLOBAL
-_gdk_win32_selection_convert_to_dib (HGLOBAL hdata,
- GdkAtom target)
-{
- GDK_NOTE (DND, {
- gchar *target_name = gdk_atom_name (target);
-
- g_print ("_gdk_win32_selection_convert_to_dib: %p %s\n",
- hdata, target_name);
- g_free (target_name);
- });
-
- if (target == _image_bmp)
+ else if (selection == GDK_SELECTION_PRIMARY)
{
- HGLOBAL hdatanew;
- SIZE_T size;
- guchar *ptr;
-
- g_return_val_if_fail (GlobalSize (hdata) >= sizeof (BITMAPFILEHEADER), NULL);
-
- /* No conversion is needed, just strip the BITMAPFILEHEADER */
- size = GlobalSize (hdata) - sizeof (BITMAPFILEHEADER);
- ptr = GlobalLock (hdata);
-
- memmove (ptr, ptr + sizeof (BITMAPFILEHEADER), size);
- GlobalUnlock (hdata);
-
- if ((hdatanew = GlobalReAlloc (hdata, size, GMEM_MOVEABLE)) == NULL)
- {
- WIN32_API_FAILED ("GlobalReAlloc");
- GlobalFree (hdata); /* The old hdata is not freed if error */
- }
- return hdatanew;
+ /* Do nothing */
}
+ else
+ {
+ gchar *sel_name = gdk_atom_name (selection);
- g_warning ("Should not happen: We provide some image format but not CF_DIB and CF_DIB is requested.");
-
- return NULL;
+ g_warning ("Unsupported generic selection %s (0x%p)", sel_name, selection);
+ g_free (sel_name);
+ }
}
--- /dev/null
+/* GDK - The GIMP Drawing Kit
+ *
+ * gdkselection-win32.h: Private Win32 specific selection object
+ *
+ * Copyright © 2017 LRN
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GDK_SELECTION_WIN32_H__
+#define __GDK_SELECTION_WIN32_H__
+
+G_BEGIN_DECLS
+
+#define _gdk_win32_selection_get() (_win32_selection)
+#define _gdk_atom_array_index(a, i) (g_array_index (a, GdkAtom, i))
+#define _gdk_win32_selection_atom(i) (_gdk_atom_array_index (_gdk_win32_selection_get ()->known_atoms, i))
+#define _gdk_cf_array_index(a, i) (g_array_index (a, UINT, i))
+#define _gdk_win32_selection_cf(i) (_gdk_cf_array_index (_gdk_win32_selection_get ()->known_clipboard_formats, i))
+
+/* Maps targets to formats or vice versa, depending on the
+ * semantics of the array that holds these.
+ * Also remembers whether the data needs to be transmuted.
+ */
+typedef struct {
+ gint format;
+ GdkAtom target;
+ gboolean transmute;
+} GdkSelTargetFormat;
+
+/* We emulate the GDK_SELECTION window properties of windows (as used
+ * in the X11 backend) by using a hash table from window handles to
+ * GdkSelProp structs.
+ */
+typedef struct {
+ guchar *data;
+ gsize length;
+ gint bitness;
+ GdkAtom target;
+} GdkSelProp;
+
+/* OLE-based DND state */
+typedef enum {
+ GDK_WIN32_DND_NONE,
+ GDK_WIN32_DND_PENDING,
+ GDK_WIN32_DND_DROPPED,
+ GDK_WIN32_DND_FAILED,
+ GDK_WIN32_DND_DRAGGING,
+} GdkWin32DndState;
+
+enum _GdkWin32AtomIndex
+{
+/* GdkAtoms: properties, targets and types */
+ GDK_WIN32_ATOM_INDEX_GDK_SELECTION = 0,
+ GDK_WIN32_ATOM_INDEX_CLIPBOARD_MANAGER,
+ GDK_WIN32_ATOM_INDEX_WM_TRANSIENT_FOR,
+ GDK_WIN32_ATOM_INDEX_TARGETS,
+ GDK_WIN32_ATOM_INDEX_DELETE,
+ GDK_WIN32_ATOM_INDEX_SAVE_TARGETS,
+ GDK_WIN32_ATOM_INDEX_UTF8_STRING,
+ GDK_WIN32_ATOM_INDEX_TEXT,
+ GDK_WIN32_ATOM_INDEX_COMPOUND_TEXT,
+ GDK_WIN32_ATOM_INDEX_TEXT_URI_LIST,
+ GDK_WIN32_ATOM_INDEX_TEXT_HTML,
+ GDK_WIN32_ATOM_INDEX_IMAGE_PNG,
+ GDK_WIN32_ATOM_INDEX_IMAGE_JPEG,
+ GDK_WIN32_ATOM_INDEX_IMAGE_BMP,
+ GDK_WIN32_ATOM_INDEX_IMAGE_GIF,
+/* DND selections */
+ GDK_WIN32_ATOM_INDEX_LOCAL_DND_SELECTION,
+ GDK_WIN32_ATOM_INDEX_DROPFILES_DND,
+ GDK_WIN32_ATOM_INDEX_OLE2_DND,
+/* Clipboard formats */
+ GDK_WIN32_ATOM_INDEX_PNG,
+ GDK_WIN32_ATOM_INDEX_JFIF,
+ GDK_WIN32_ATOM_INDEX_GIF,
+ GDK_WIN32_ATOM_INDEX_CF_DIB,
+ GDK_WIN32_ATOM_INDEX_CFSTR_SHELLIDLIST,
+ GDK_WIN32_ATOM_INDEX_CF_TEXT,
+ GDK_WIN32_ATOM_INDEX_CF_UNICODETEXT,
+ GDK_WIN32_ATOM_INDEX_LAST
+};
+
+typedef enum _GdkWin32AtomIndex GdkWin32AtomIndex;
+
+enum _GdkWin32CFIndex
+{
+ GDK_WIN32_CF_INDEX_PNG = 0,
+ GDK_WIN32_CF_INDEX_JFIF,
+ GDK_WIN32_CF_INDEX_GIF,
+ GDK_WIN32_CF_INDEX_UNIFORMRESOURCELOCATORW,
+ GDK_WIN32_CF_INDEX_CFSTR_SHELLIDLIST,
+ GDK_WIN32_CF_INDEX_HTML_FORMAT,
+ GDK_WIN32_CF_INDEX_TEXT_HTML,
+ GDK_WIN32_CF_INDEX_IMAGE_PNG,
+ GDK_WIN32_CF_INDEX_IMAGE_JPEG,
+ GDK_WIN32_CF_INDEX_IMAGE_BMP,
+ GDK_WIN32_CF_INDEX_IMAGE_GIF,
+ GDK_WIN32_CF_INDEX_TEXT_URI_LIST,
+ GDK_WIN32_CF_INDEX_UTF8_STRING,
+ GDK_WIN32_CF_INDEX_LAST
+};
+
+typedef enum _GdkWin32CFIndex GdkWin32CFIndex;
+
+#define GDK_TYPE_WIN32_SELECTION (gdk_win32_selection_get_type ())
+#define GDK_WIN32_SELECTION(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), GDK_TYPE_WIN32_SELECTION, GdkWin32Selection))
+#define GDK_WIN32_SELECTION_CLASS(c) (G_TYPE_CHECK_CLASS_CAST ((c), GDK_TYPE_WIN32_SELECTION, GdkWin32SelectionClass))
+#define GDK_IS_WIN32_SELECTION(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), GDK_TYPE_WIN32_SELECTION))
+#define GDK_IS_WIN32_SELECTION_CLASS(c) (G_TYPE_CHECK_CLASS_TYPE ((c), GDK_TYPE_WIN32_SELECTION))
+#define GDK_WIN32_SELECTION_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), GDK_TYPE_WIN32_SELECTION, GdkWin32SelectionClass))
+
+typedef struct _GdkWin32Selection GdkWin32Selection;
+typedef struct _GdkWin32SelectionClass GdkWin32SelectionClass;
+
+/* This object is just a sink to hold all the selection- and dnd-related data
+ * that otherwise would be in global variables.
+ */
+struct _GdkWin32Selection
+{
+ GObject *parent_instance;
+ GHashTable *sel_prop_table;
+ GdkSelProp *dropfiles_prop;
+ /* We store the owner of each selection in this table. Obviously, this only
+ * is valid intra-app, and in fact it is necessary for the intra-app DND to work.
+ */
+ GHashTable *sel_owner_table;
+
+ /* GdkAtoms for well-known image formats */
+ GdkAtom *known_pixbuf_formats;
+ int n_known_pixbuf_formats;
+
+ /* GArray of GdkAtoms for various known Selection and DnD strings.
+ * Size is guaranteed to be at least GDK_WIN32_ATOM_INDEX_LAST
+ */
+ GArray *known_atoms;
+
+ /* GArray of UINTs for various known clipboard formats.
+ * Size is guaranteed to be at least GDK_WIN32_CF_INDEX_LAST.
+ */
+ GArray *known_clipboard_formats;
+
+ GdkWin32DndState dnd_target_state;
+ GdkWin32DndState dnd_source_state;
+
+ /* Holds a reference to the data object for the target drop site.
+ */
+ IDataObject *dnd_data_object_target;
+
+ /* Carries DnD target context from idroptarget_*() to convert_selection() */
+ GdkDragContext *target_drag_context;
+
+ /* Carries W32 format ID from idataobject_getdata() to property_change() */
+ UINT property_change_format;
+ /* Carries the W32-wrapped data between idataobject_getdata() and property_change() */
+ LPSTGMEDIUM property_change_data;
+ /* Carries the transmute field of the GdkSelTargetFormat from from idataobject_getdata() to property_change() */
+ gboolean property_change_transmute;
+
+ /* TRUE when we are emptying the clipboard ourselves */
+ gboolean ignore_destroy_clipboard;
+
+ /* Array of GdkSelTargetFormats describing the targets supported by the clipboard selection */
+ GArray *clipboard_selection_targets;
+
+ /* Same for the DnD selection (applies for both LOCAL and OLE2 DnD) */
+ GArray *dnd_selection_targets;
+
+ /* If TRUE, then we queued a GDK_SELECTION_REQUEST with TARGETS
+ * target. This field is checked to prevent queueing
+ * multiple selection requests.
+ */
+ gboolean targets_request_pending;
+
+ /* The handle that was given to OpenClipboard().
+ * NULL is a valid handle,
+ * INVALID_HANDLE_VALUE means that the clipboard is closed.
+ */
+ HWND clipboard_opened_for;
+
+ /* A target-keyed hash table of GArrays of GdkSelTargetFormats describing compatibility formats for a target */
+ GHashTable *compatibility_formats;
+ /* A format-keyed hash table of GArrays of GdkAtoms describing compatibility targets for a format */
+ GHashTable *compatibility_targets;
+};
+
+struct _GdkWin32SelectionClass
+{
+ GObjectClass parent_class;
+};
+
+GType gdk_win32_selection_get_type (void) G_GNUC_CONST;
+
+void _gdk_win32_clear_clipboard_queue ();
+gchar * _gdk_win32_get_clipboard_format_name (UINT fmt,
+ gboolean *is_predefined);
+void _gdk_win32_add_format_to_targets (UINT format,
+ GArray *array,
+ GList **list);
+gint _gdk_win32_add_target_to_selformats (GdkAtom target,
+ GArray *array);
+void _gdk_win32_selection_property_change (GdkWin32Selection *win32_sel,
+ GdkWindow *window,
+ GdkAtom property,
+ GdkAtom type,
+ gint format,
+ GdkPropMode mode,
+ const guchar *data,
+ gint nelements);
+
+#endif /* __GDK_SELECTION_WIN32_H__ */
--- /dev/null
+/* GDK - The GIMP Drawing Kit
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef __GDK_WIN32_DND_PRIVATE_H__
+#define __GDK_WIN32_DND_PRIVATE_H__
+
+#if !defined (__GDKWIN32_H_INSIDE__) && !defined (GDK_COMPILATION)
+#error "Only <gdk/gdkwin32.h> can be included directly."
+#endif
+
+#include <gdk/gdk.h>
+
+G_BEGIN_DECLS
+
+struct _GdkWin32DragContext
+{
+ GdkDragContext context;
+ GdkWindow *ipc_window;
+ GdkWindow *drag_window;
+ GdkCursor *cursor;
+ GdkSeat *grab_seat;
+ GdkDragAction actions;
+ GdkDragAction current_action;
+
+ guint drag_status : 4; /* Current status of drag */
+ guint drop_failed : 1; /* Whether the drop was unsuccessful */
+ guint has_image_format : 1;
+ guint has_text_uri_list : 1;
+ guint has_shell_id_list : 1;
+ guint has_unicodetext : 1;
+ guint has_cf_png : 1;
+ guint has_cf_dib : 1;
+ guint has_gif : 1;
+ guint has_jfif : 1;
+
+ guint scale; /* Temporarily caches the HiDPI scale */
+ gint hot_x; /* Hotspot offset from the top-left of the drag-window, scaled (can be added to GDK space coordinates) */
+ gint hot_y;
+ gint last_x; /* Coordinates from last event, in GDK space */
+ gint last_y;
+ gint start_x; /* Coordinates of the drag start, in GDK space */
+ gint start_y;
+ DWORD last_key_state; /* Key state from last event */
+
+ /* Just like context->targets, but an array, and with format IDs
+ * stored inside.
+ */
+ GArray *droptarget_format_target_map;
+};
+
+struct _GdkWin32DragContextClass
+{
+ GdkDragContextClass parent_class;
+};
+
+G_END_DECLS
+
+#endif /* __GDK_WIN32_DND_PRIVATE_H__ */
gint n_targets,
GdkAtom *targets);
+#if defined (GTK_COMPILATION) || defined (GDK_COMPILATION)
+#define gdk_win32_selection_clear_targets gdk_win32_selection_clear_targets_libgtk_only
+GDK_AVAILABLE_IN_ALL
+void gdk_win32_selection_clear_targets (GdkDisplay *display,
+ GdkAtom selection);
+#endif
+
GDK_AVAILABLE_IN_ALL
GdkWindow * gdk_win32_window_foreign_new_for_display (GdkDisplay *display,
HWND anid);
if (impl->type_hint == GDK_WINDOW_TYPE_HINT_UTILITY)
dwExStyle |= WS_EX_TOOLWINDOW;
+ /* WS_EX_TRANSPARENT means "try draw this window last, and ignore input".
+ * It's the last part we're after. We don't want DND indicator to accept
+ * input, because that will make it a potential drop target, and if it's
+ * under the mouse cursor, this will kill any DND.
+ */
+ if (impl->type_hint == GDK_WINDOW_TYPE_HINT_DND)
+ dwExStyle |= WS_EX_TRANSPARENT;
+
klass = RegisterGdkClass (window->window_type, impl->type_hint);
wtitle = g_utf8_to_utf16 (title, -1, NULL, NULL, NULL);
#endif
#endif
+#ifdef GDK_WINDOWING_WIN32
+#include <gdk/win32/gdkwin32.h>
+#endif
+
#ifdef GDK_WINDOWING_WAYLAND
#include <gdk/wayland/gdkwayland.h>
#endif
#endif
#ifdef GDK_WINDOWING_WAYLAND
GDK_IS_WAYLAND_DISPLAY (gtk_widget_get_display (source_widget)) ||
+#endif
+#ifdef GDK_WINDOWING_WIN32
+ GDK_IS_WIN32_DISPLAY (gtk_widget_get_display (source_widget)) ||
#endif
FALSE;
}
if (GDK_IS_WAYLAND_DISPLAY (gtk_widget_get_display (widget)))
gdk_wayland_selection_clear_targets (gtk_widget_get_display (widget), selection);
#endif
+#ifdef GDK_WINDOWING_WIN32
+ if (GDK_IS_WIN32_DISPLAY (gtk_widget_get_display (widget)))
+ gdk_win32_selection_clear_targets (gtk_widget_get_display (widget), selection);
+#endif
lists = g_object_get_data (G_OBJECT (widget), gtk_selection_handler_key);